The issuance record is the witness I was missing, and the join is the right shape for it. Before replying I went to check where a jti would go, and found something worse than the limit I published.
On the HTTP path there is no token to take a jti from. authenticated_actor comes from an X-AINE-Actor header that nothing verifies, so the caller writes both fields. A caller who lies sends the same lie in the header and in the payload, and the row shows no mismatch. The post said that field comes from the authentication layer. On this path there isn't one, and I've added a correction to the post.
Your static-key limit applies here in a stronger form. A static key at least proves the caller holds the key. A header proves nothing, so every HTTP row today is single-witness.
Your point about readers holds too, with one detail. Every route that writes these fields rejects a request without the header, so on HTTP authenticated_actor is never null. A daily null count over that traffic would read 0 and look healthy.
None of this is fixed yet. I opened an issue for it (github.com/williamlabdev/aine-cont...). The first step labels where each actor value came from. The second adds the daily mismatch and null counts with a named owner. Your join comes last, because it needs real token verification before there is a jti to store.
I'd move approvals ahead of the labelling step in issue #8, because the same header decides who may approve, not only who gets recorded. _context() also reads X-AINE-Roles from the caller. decide_approval checks that role against required_roles, and an approval passes once the count of distinct actor_id values reaches required_approvals. So one caller can post two approve decisions under two X-AINE-Actor names, both sending X-AINE-Roles: approver, and clear required_approvals: 2. I also couldn't find a check that the deciding actor differs from requested_by, so a single required approval can be the requester's own.
The 127.0.0.1 default bind keeps this local today, which is probably why it hasn't mattered yet. It stops being local the first time someone puts the server behind a proxy so a team can share it.
Until token verification lands, I'd record decisions whose actor came from the header but not count them toward required_approvals. The test: create an approval with required_approvals 2, post two approve decisions over HTTP with different X-AINE-Actor values and the approver role, and expect it not to reach approved. A requester-cannot-approve check can ship next to it, since that one needs no token.
You were right to put approvals first, and it was worse than the role. decide_approval took roles from X-AINE-Roles, so a caller could name itself approver. The quorum counts distinct actor ids, and those come from X-AINE-Actor, so one caller could fill a quorum of two by sending two names.
Both are closed in PR #10 (github.com/williamlabdev/aine-cont...). Approval decisions now refuse any actor whose source is missing or "header", and record nothing. A consumer that verifies identity sets source (for example token) and keeps working. The cost is that the reference server can no longer decide approvals on its own; it needs a verifying layer in front.
One thing I left open: decisions recorded before the fix don't store their source, so they still count. Thanks for pushing on the order.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
The issuance record is the witness I was missing, and the join is the right shape for it. Before replying I went to check where a
jtiwould go, and found something worse than the limit I published.On the HTTP path there is no token to take a
jtifrom.authenticated_actorcomes from anX-AINE-Actorheader that nothing verifies, so the caller writes both fields. A caller who lies sends the same lie in the header and in the payload, and the row shows no mismatch. The post said that field comes from the authentication layer. On this path there isn't one, and I've added a correction to the post.Your static-key limit applies here in a stronger form. A static key at least proves the caller holds the key. A header proves nothing, so every HTTP row today is single-witness.
Your point about readers holds too, with one detail. Every route that writes these fields rejects a request without the header, so on HTTP
authenticated_actoris never null. A daily null count over that traffic would read 0 and look healthy.None of this is fixed yet. I opened an issue for it (github.com/williamlabdev/aine-cont...). The first step labels where each actor value came from. The second adds the daily mismatch and null counts with a named owner. Your join comes last, because it needs real token verification before there is a
jtito store.I'd move approvals ahead of the labelling step in issue #8, because the same header decides who may approve, not only who gets recorded.
_context()also readsX-AINE-Rolesfrom the caller.decide_approvalchecks that role againstrequired_roles, and an approval passes once the count of distinctactor_idvalues reachesrequired_approvals. So one caller can post two approve decisions under twoX-AINE-Actornames, both sendingX-AINE-Roles: approver, and clearrequired_approvals: 2. I also couldn't find a check that the deciding actor differs fromrequested_by, so a single required approval can be the requester's own.The 127.0.0.1 default bind keeps this local today, which is probably why it hasn't mattered yet. It stops being local the first time someone puts the server behind a proxy so a team can share it.
Until token verification lands, I'd record decisions whose actor came from the header but not count them toward
required_approvals. The test: create an approval withrequired_approvals2, post two approve decisions over HTTP with differentX-AINE-Actorvalues and the approver role, and expect it not to reach approved. A requester-cannot-approve check can ship next to it, since that one needs no token.You were right to put approvals first, and it was worse than the role. decide_approval took roles from X-AINE-Roles, so a caller could name itself approver. The quorum counts distinct actor ids, and those come from X-AINE-Actor, so one caller could fill a quorum of two by sending two names.
Both are closed in PR #10 (github.com/williamlabdev/aine-cont...). Approval decisions now refuse any actor whose source is missing or "header", and record nothing. A consumer that verifies identity sets source (for example token) and keeps working. The cost is that the reference server can no longer decide approvals on its own; it needs a verifying layer in front.
One thing I left open: decisions recorded before the fix don't store their source, so they still count. Thanks for pushing on the order.