Symptom
A metadata write made by an authenticated admin is attributed to nobody.
PUT /api/v1/meta/<type>/<name> with an admin bearer token → the audit row records actor: 'system' and the history row records actor: null.- The real identity appears only when the caller sends
X-Actorby hand.
The practical consequence: every audit row written through the normal console/API path names system, so the trail cannot answer "who changed this" for any client that does not know to add a non-standard header.
Root cause
packages/rest/src/rest-server.ts computes the actor as a three-way fallback, at five sites (lines 5846-5847, 5965-5966, 6110-6111, 6164-6165, 6516-6517 on origin/main):
const actorHeader = req.headers?.['x-actor'] ?? req.headers?.['X-Actor']
?? req.user?.id ?? req.userId;
The header is the only limb that resolves on this route: req.user and req.userId are unset on the /meta PUT path, so with no X-Actor the expression yields undefined and the downstream default ('system' for audit, null for history) takes over. The bearer token is validated — it just never lands on req.user for these handlers.
Stale-premise check: re-verified on objectstackorigin/main (00e9196) — all five sites still carry the identical fallback chain.
Reproduction
- Boot the showcase with writable runtime packages; obtain an admin bearer token.
PUT /api/v1/meta/<type>/<name> with Authorization: Bearer … and noX-Actor.GET /api/v1/meta/<type>/<name>/audit → the row reads actor: 'system'; /history reads actor: null.- Repeat with
X-Actor: <id> → the row carries that id, proving the write path itself records whatever the fallback resolves.
Scope
Separate from #7748 (publish/rollback write no audit row at all): different file, different failure, and this one survives that fix — once publish and rollback start writing rows, they will write them stamped system for the same reason.
Source
Extracted from the QA run #7695 (framework 92f26f7, console 09987b680).
Symptom
A metadata write made by an authenticated admin is attributed to nobody.
PUT /api/v1/meta/<type>/<name>with an admin bearer token → the audit row recordsactor: 'system'and the history row recordsactor: null.X-Actorby hand.The practical consequence: every audit row written through the normal console/API path names
system, so the trail cannot answer "who changed this" for any client that does not know to add a non-standard header.Root cause
packages/rest/src/rest-server.tscomputes the actor as a three-way fallback, at five sites (lines 5846-5847, 5965-5966, 6110-6111, 6164-6165, 6516-6517 onorigin/main):The header is the only limb that resolves on this route:
req.userandreq.userIdare unset on the/metaPUT path, so with noX-Actorthe expression yieldsundefinedand the downstream default ('system'for audit,nullfor history) takes over. The bearer token is validated — it just never lands onreq.userfor these handlers.Stale-premise check: re-verified on
objectstackorigin/main(00e9196) — all five sites still carry the identical fallback chain.Reproduction
PUT /api/v1/meta/<type>/<name>withAuthorization: Bearer …and noX-Actor.GET /api/v1/meta/<type>/<name>/audit→ the row readsactor: 'system';/historyreadsactor: null.X-Actor: <id>→ the row carries that id, proving the write path itself records whatever the fallback resolves.Scope
Separate from #7748 (publish/rollback write no audit row at all): different file, different failure, and this one survives that fix — once publish and rollback start writing rows, they will write them stamped
systemfor the same reason.Source
Extracted from the QA run #7695 (framework 92f26f7, console 09987b680).