Proton's media plugins load in boxes; billing webhooks say what they did (#408)

- **Guest image (#405):** carries `libvpx`, `speex`, `libtheora`,
`libvdpau` and `jsoncpp`, which Proton's GStreamer plugins link. Without
them, `libgstlibav` and `libgstspeex` failed to load in every box on
every Proton shipped so far. Verified: every library both published
Protons need is now provided.
- **Billing (#406):** every Polar webhook logs its type, the team it
resolved to, the plan and status, whether it changed anything, and the
subscription and customer ids. Logging only.

Cut from prod rather than opened from dev: after squash merges the two
histories conflict, though the files differ only in these four. This
branch's tree is identical to dev's.

After merge, the guest image is published from this prod commit. Host
agent 0.2.18 then makes hosts pull it.
This commit is contained in:
Wanjohi
2026-10-15 16:33:56 +00:00
committed by GitHub
parent 351a2d890a
commit bf65df7e1a
4 changed files with 119 additions and 3 deletions
+38 -2
View File
@@ -78,6 +78,10 @@ export namespace BillingApi {
});
const delivery = Polar.receive({ body, headers });
const ids = {
subscription: delivery.subscriptionId,
customer: delivery.customerId
};
// Acknowledged rather than refused. A delivery we cannot act on is
// still a delivery that arrived intact, and answering an error
@@ -85,10 +89,28 @@ export namespace BillingApi {
// never become actionable.
if (!delivery.teamId || !delivery.standing) {
// Said, because a paid subscription that matches no team is
// somebody paying for nothing, and a 200 alone hides it.
// somebody paying for nothing, and a 200 alone hides it. With
// the provider's ids, so whoever reads it can find the customer
// in the provider's dashboard and set the team's plan by hand:
// a subscription made before checkouts carried the team id
// arrives like this for as long as it lives.
if (delivery.standing?.plan === 'paid') {
console.error('a paid subscription matched no team', { type: delivery.type });
console.error(
'a paid subscription matched no team; find the customer in the provider dashboard and set the team plan by hand',
{ type: delivery.type, team: delivery.teamId, ...ids }
);
}
// Every delivery is said, applied or not. A webhook that
// changes nothing looks exactly like one that worked from the
// outside, and a cancellation on 2026-10-15 was only found to
// have missed by reading the team's row.
console.log('billing webhook', {
type: delivery.type,
team: delivery.teamId,
standing: delivery.standing,
applied: false,
...ids
});
return c.json({ data: { applied: false, type: delivery.type } });
}
@@ -97,6 +119,20 @@ export namespace BillingApi {
plan: delivery.standing.plan,
subscriptionStatus: delivery.standing.status
});
if (!updated) {
console.error('a subscription names a team that does not exist', {
type: delivery.type,
team: delivery.teamId,
...ids
});
}
console.log('billing webhook', {
type: delivery.type,
team: delivery.teamId,
standing: delivery.standing,
applied: Boolean(updated),
...ids
});
return c.json({
data: { applied: Boolean(updated), type: delivery.type }
+11
View File
@@ -396,11 +396,22 @@ RUN pacman -Syu --noconfirm --needed \
pipewire pipewire-audio wireplumber opus \
python libunwind sdl2-compat \
python-xlib python-urllib3 python-pyzstd \
libvpx speex libtheora libvdpau jsoncpp \
strace lsof less \
&& rm -f /usr/share/libalpm/hooks/dbus-reload.hook \
&& pacman -Rdd --noconfirm systemd systemd-sysvcompat \
&& pacman -Scc --noconfirm
# `libvpx`, `speex`, `libtheora`, `libvdpau` and `jsoncpp` are not ours at all:
# they are what Proton's own GStreamer plugins link against, and Proton does
# not carry them. Without them `libgstlibav.so` and `libgstspeex.so` fail to
# load (`libvpx.so.12: cannot open shared object file`), so a game whose videos
# go through Wine's media stack plays them as nothing. Measured 2026-10-15 by
# listing every NEEDED library of the published Proton against this image;
# these five were the whole gap, for the stock build and the patched one
# alike. They track the sonames Proton was built against, so a Proton built
# on a newer Arch can need this list rebuilt with it.
#
# `strace`, `lsof` and `less` are for the operator's shell (`neslet box exec`):
# a root shell in a box that cannot trace a process, list what it has open or
# page through a log is the situation that feature exists to get out of. They
+44
View File
@@ -235,6 +235,50 @@ describe('Both signing schemes', () => {
expect(delivery.standing).toEqual({ plan: 'paid', status: 'active' });
});
test('the provider\u2019s ids travel with a delivery, in either spelling', async () => {
// They are what a person looks the customer up by when a delivery
// matched no team, as a subscription made before checkouts carried the
// team id does for as long as it lives.
const { Webhook } = await import('standardwebhooks');
const secret = `whsec_${Buffer.from('d'.repeat(32)).toString('base64')}`;
configure({ POLAR_WEBHOOK_SECRET: secret });
const send = (payload: object, id: string) => {
const body = JSON.stringify(payload);
const timestamp = new Date();
return Polar.receive({
body,
headers: {
'webhook-id': id,
'webhook-timestamp': Math.floor(timestamp.getTime() / 1000).toString(),
'webhook-signature': new Webhook(secret).sign(id, timestamp, body)
}
});
};
const unmatched = send(
{
type: 'subscription.canceled',
data: { id: 'sub_1', customer_id: 'cus_1', customer: { external_id: null }, product_id: PAID }
},
'msg_ids_snake'
);
expect(unmatched.teamId).toBeNull();
expect(unmatched.standing).toEqual({ plan: 'paid', status: 'canceled' });
expect(unmatched.subscriptionId).toBe('sub_1');
expect(unmatched.customerId).toBe('cus_1');
// Not a subscription event: its own id is the order's, and the
// subscription is a field on it.
const order = send(
{ type: 'order.refunded', data: { id: 'ord_1', subscriptionId: 'sub_2', customer: { id: 'cus_2' } } },
'msg_ids_order'
);
expect(order.subscriptionId).toBe('sub_2');
expect(order.customerId).toBe('cus_2');
expect(order.standing).toBeNull();
});
test('a tampered body under a valid-looking signature is refused', () => {
const secret = `whsec_${Buffer.from('a'.repeat(32)).toString('base64')}`;
configure({ POLAR_WEBHOOK_SECRET: secret });
+26 -1
View File
@@ -286,6 +286,14 @@ export namespace Polar {
/** The team this is about, from the customer's external id. */
teamId: string | null;
standing: Standing | null;
/**
* The provider's own ids for the subscription and the customer. Nothing
* here acts on them; they are what a person searches the provider's
* dashboard for when a delivery matched no team and has to be applied by
* hand.
*/
subscriptionId: string | null;
customerId: string | null;
}
/**
@@ -372,7 +380,24 @@ export namespace Polar {
product?.id ??
null;
return { type: event.type, teamId, standing: standingFor(event.type, productId) };
// A subscription event's own id is the subscription's; anything else
// about one carries it as a field.
const str = (v: unknown) => (typeof v === 'string' ? v : null);
const subscriptionId = event.type.startsWith('subscription.')
? str(data.id)
: (str(data.subscriptionId) ?? str(data.subscription_id));
const customerId =
str(data.customerId) ??
str(data.customer_id) ??
str((data.customer as { id?: unknown } | undefined)?.id);
return {
type: event.type,
teamId,
standing: standingFor(event.type, productId),
subscriptionId,
customerId
};
}
);
}