registry: a second factor before publishing tokens (slice 6 of #16) #228

Closed
opened 2026-10-08 21:26:56 +00:00 by orion · 7 comments
Collaborator

Goal

Enforce RELEASE.md 7.6's "mandatory second factor (TOTP or a passkey) for every account that can publish": such an account cannot mint or use a publishing token without an enrolled factor.

Owner decision (recorded on #16)

  • A passkey counts as the second factor on its own; a provider sign-in needs TOTP or a passkey on top.
  • Minting or using publishing tokens requires an enrolled factor.
  • Tokens that already exist keep working until their expiry (at most 365 days); no cut-over at deployment.

Pinned records

Acceptance criteria

  • "Can publish" is defined in code once: a member of any owner with the owner or publisher role.
  • POST /api/1/tokens with the publish action (and tokens minted from the account page) is refused with a clear message while the account has no enrolled second factor; a token minted after enrolment records that it was.
  • A publish with a token minted after this slice, by an account that has since removed its last second factor, is refused; tokens minted before this slice keep working until their expiry, as decided.
  • Removing the last second factor of an account that can publish is refused, or revokes its publishing tokens (the PR picks one and documents it in RELEASE.md).
  • Trusted publishing (OIDC exchange) is unaffected: it uses no stored token.
  • Tests: every rule once as it works and once as it is refused; torb test tools/registry/tests green natively and in the VM.
  • RELEASE.md 7.6 and the write-service table, and tools/registry/README.md updated; "Not built yet" no longer lists sign-in or a second factor.

Seed impact

None: TorbScript in tools/registry; no syntax change, no native, no driver flag, no looked-up std name.

Size and dependencies

One PR, one or two Conventional Commits. Depends on slice 4 (#226, passkeys) and slice 5 (#227, TOTP). Parent: #16. Not ready until both have landed. Security-relevant: architect review of the diff.

## Goal Enforce RELEASE.md 7.6's "mandatory second factor (TOTP or a passkey) for every account that can publish": such an account cannot mint or use a publishing token without an enrolled factor. ## Owner decision (recorded on #16) - A passkey counts as the second factor on its own; a provider sign-in needs TOTP or a passkey on top. - Minting or using publishing tokens requires an enrolled factor. - Tokens that already exist keep working until their expiry (at most 365 days); no cut-over at deployment. ## Pinned records - [RELEASE.md 7.6 Security](https://git.torb.dev/torbscript/language/src/commit/45be87a8bdba3d0a00212c7a82fa8e451bef398c/docs/design/RELEASE.md#L1029) - [RELEASE.md, the write service as built (Tokens row)](https://git.torb.dev/torbscript/language/src/commit/45be87a8bdba3d0a00212c7a82fa8e451bef398c/docs/design/RELEASE.md#L1283) ## Acceptance criteria - [ ] "Can publish" is defined in code once: a member of any owner with the `owner` or `publisher` role. - [ ] `POST /api/1/tokens` with the `publish` action (and tokens minted from the account page) is refused with a clear message while the account has no enrolled second factor; a token minted after enrolment records that it was. - [ ] A publish with a token minted after this slice, by an account that has since removed its last second factor, is refused; tokens minted before this slice keep working until their expiry, as decided. - [ ] Removing the last second factor of an account that can publish is refused, or revokes its publishing tokens (the PR picks one and documents it in RELEASE.md). - [ ] Trusted publishing (OIDC exchange) is unaffected: it uses no stored token. - [ ] Tests: every rule once as it works and once as it is refused; `torb test tools/registry/tests` green natively and in the VM. - [ ] RELEASE.md 7.6 and the write-service table, and `tools/registry/README.md` updated; "Not built yet" no longer lists sign-in or a second factor. ## Seed impact None: TorbScript in `tools/registry`; no syntax change, no native, no driver flag, no looked-up std name. ## Size and dependencies One PR, one or two Conventional Commits. Depends on slice 4 (#226, passkeys) and slice 5 (#227, TOTP). Parent: #16. Not `ready` until both have landed. Security-relevant: architect review of the diff.
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), slice 6 of #16 filed by the split, not a duplicate, no collision with a design record, no seed impact. next: waiting (slices 4 #226 and 5 #227 have not landed) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), slice 6 of #16 filed by the split, not a duplicate, no collision with a design record, no seed impact. next: waiting (slices 4 #226 and 5 #227 have not landed) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] intake: dependencies #226 and #227 have not landed (#227 is being refined now). next: waiting (dependencies #226, #227) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: dependencies #226 and #227 have not landed (#227 is being refined now). next: waiting (dependencies #226, #227) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] correction: slice 4 (#226) has landed; only slice 5 (#227) is outstanding (being refined now). next: waiting (dependency #227) until 2026-10-10T04:00:00Z

[loop:torbscript] correction: slice 4 (#226) has landed; only slice 5 (#227) is outstanding (being refined now). next: waiting (dependency #227) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] intake: unchanged. #227 waits on #264 (std/cipher), so this slice follows both. next: waiting (dependencies #227, #264) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: unchanged. #227 waits on #264 (std/cipher), so this slice follows both. next: waiting (dependencies #227, #264) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] decided by the loop: dependencies #226 and #227 landed (main green on 173c9c66), criteria complete, so set ready. claim implement by session:loop-torbscript-20261009 until 2026-10-09T22:04Z

[loop:torbscript] decided by the loop: dependencies #226 and #227 landed (main green on 173c9c66), criteria complete, so set ready. claim implement by session:loop-torbscript-20261009 until 2026-10-09T22:04Z
Author
Collaborator

[loop:torbscript] implement: PR #273 opened, CI green, mint and use of publishing tokens now need a second factor (passkey alone counts); removing the last factor of a publisher is refused (409), documented in RELEASE.md 7.6. Known limits listed in the PR (owners action and operator/first tokens not bound). next: review (waiting on the merge gate argus). Claim released.

[loop:torbscript] implement: PR https://git.torb.dev/torbscript/language/pulls/273 opened, CI green, mint and use of publishing tokens now need a second factor (passkey alone counts); removing the last factor of a publisher is refused (409), documented in RELEASE.md 7.6. Known limits listed in the PR (owners action and operator/first tokens not bound). next: review (waiting on the merge gate argus). Claim released.
argus closed this issue 2026-10-09 16:44:29 +00:00
Author
Collaborator

[loop:torbscript] close: PR #273 approved and merged by argus as d87348ed; main CI green on that commit. Follow-up for the three limits the review raised: #274. Claim released.

[loop:torbscript] close: PR https://git.torb.dev/torbscript/language/pulls/273 approved and merged by argus as d87348ed; main CI green on that commit. Follow-up for the three limits the review raised: #274. Claim released.
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
torbscript/language#228
No description provided.