Skip to content

feat(tokens): sign canonical meta.machineId on History assertions - #113

Merged
khaliqgant merged 4 commits into
mainfrom
feat/relayhistory-assertion-machine-id
Oct 31, 2026
Merged

khaliqgant merged 4 commits into
mainfrom
feat/relayhistory-assertion-machine-id

Conversation

@AgentRelayBot

@AgentRelayBot AgentRelayBot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Accept an optional machineId on POST /v1/tokens/relayhistory-assertion and sign it as meta.machineId when present.
  • Validate supplied values byte-for-byte against the shared manifest machine-id contract: 1-128 characters of A-Za-z0-9._-, no leading ., and no relay_ alias prefix. Invalid values fail with invalid_machine_id; values are never trimmed or case-folded.
  • Record the supplied ID in the issuance audit row. An omitted ID produces no claim and records machineIdMissing: "true".
  • Keep issuance compatible with read-only and control-plane History paths that have no meaningful courier machine.
  • Preserve the dedicated plain API-key authority boundary. Bearer/admin callers cannot mint these assertions or delegate the broker scope.

Security model and compensating control

RelayAuth validates and signs a supplied machine ID but cannot independently know which courier machine a control-plane request represents. The dedicated assertion-key holder remains responsible for deriving the ID from an authenticated courier session. The assertion key must never be given to a courier.

The route already accepts orgId, workspaceId, and sponsorId from that dedicated broker. This existing cross-org trust boundary is not widened here and remains a residual risk for the control matrix.

Machine binding for writes is a consumer control. The 96Bulls ingest path fails closed before mutation when meta.machineId is absent or when it differs from either manifest.session.machine_id or manifest.writer.delivery_instance_id:

The limited claimless compatibility window is read-only, audited, and capped at 72 hours; migration assertions use a separate grant-bound path. RelayHistory Cloud does not currently enforce machine binding, so this PR does not claim that control for Cloud. Audit reviews of machineIdMissing must correlate grantedScopes and the consumer operation because legitimate non-machine mints also omit the claim.

Exact-head validation (92a8da72d1a28bed7fbb61ca328e86e42ccb5ff2)

The final commit is comment-only; the executed validation was on its code-identical parent 2dd88a12cf8f15d0efb6880111527e20722bdd6c:

  • Focused token-route tests: 235 pass, 0 fail.
  • npm test: 16/16 workspace tasks; server 740/740; root follow-up 8/8.
  • npm run typecheck: 11/11.
  • npx turbo build --force: 9/9, 0 cache hits.
  • helm lint charts/relayauth: 1 chart, 0 failures.
  • Combined Helm render retained private History-origin plus session-directory issuer/signing-key settings and emitted no removed compatibility flag.
  • git diff --check: clean.

No package was published, no service was deployed, no live system was changed, and no credentials were read or written. Hosted exact-head checks and independent review are required before merge.


Agent Relay sessions

  • claude session 719b5435 · opened via gh pr create · last active 2026-10-30
  • codex session 01a18a0b · contributor · pushed commits · last active 2026-10-31

POST /v1/tokens/relayhistory-assertion now takes a machineId and signs it
as meta.machineId, so ingest can bind a courier's token to the machine
(the manifest key's m= component) on manifest POST and status.

- Canonical form: 1-128 chars of [A-Za-z0-9._-], no leading '.', no
  relay_ alias prefix, exact bytes. Anything needing trimming, case
  folding or alias stripping is refused (400 invalid_machine_id), never
  rewritten. null, "" and non-strings are malformed, not missing.
- Missing machineId: 400 machineId_required, unless the default-off
  compat flag RELAYAUTH_ALLOW_RELAYHISTORY_ASSERTION_WITHOUT_MACHINE_ID
  is exactly "true" (chart: relayhistory.allowMissingMachineId). Such
  tokens have no meta.machineId and their audit row is flagged
  machineIdMissing: "true".
- The audit row records machineId.
- Trust: RelayAuth validates form only and signs what the holder of the
  dedicated relayauth:assertion:create:relayhistory key (Cloud's broker)
  asserts after its own authorization, as for orgId/workspaceId/
  sponsorId. The route still requires that key over x-api-key (bearer
  refused), so couriers and other callers cannot choose it. Documented
  in docs/self-hosting.md.
- Not widened: org/workspace/sponsor handling is unchanged. The known
  cross-org property of this route (orgId/sponsorId from the body) is
  left as is; a separate hardening is proposed in the PR.

Rollout: required by default, so the broker must send machineId before
this deploys, or the compat flag must be on for the window.

Review: adversarial pass (no new caller can mint, malformed/alias/
whitespace/unicode/oversize values refused under both flag states, flag
enables only on exact "true", injected body meta/org fields ignored,
claims org/wks/sponsor unchanged) and contextual pass (existing tests,
selfhost smoke script, Helm chart, env wiring, docs updated).

Validation (local; hosted CI runs on PRs to main):
- relayhistory-assertion tests: 114/114 (27 new)
- npm test (turbo 16/16): server 714/714, sdk 169/169, ai 70/70,
  core 19/19, migrate 18/18, relayauth 12/12, types 1/1, root 8/8
- server test:e2e: 40 pass, 0 fail, 93 skipped
- turbo typecheck --force 11/11, turbo build --force 9/9
- helm template renders the env only with allowMissingMachineId=true;
  bash -n scripts/selfhost-smoke.sh OK

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3e73163a-b4d7-4852-b38d-b256189e2c69
Commits

Reviewing files that changed from the base of the PR and between e2d72f7 and 92a8da7.

Files selected for processing: 5
  • docs/self-hosting.md
  • packages/server/src/__tests__/selfhost-keys-issuer-bootstrap.test.ts
  • packages/server/src/__tests__/tokens-route.test.ts
  • packages/server/src/routes/tokens.ts
  • scripts/selfhost-smoke.sh

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

RelayHistory assertion requests now accept an optional machine ID. The route validates supplied values, adds accepted IDs to signed assertion metadata and audit records, and marks omitted IDs in the audit record. Tests and self-hosting materials cover the new field.

Changes: RelayHistory machine binding

Layer / File(s) Summary
Request contract and validation
packages/server/src/routes/tokens.ts
The assertion request accepts an optional machineId. Validation accepts 1–128 characters from A-Za-z0-9._-, except values starting with . or relay_. Invalid values return 400 invalid_machine_id.
Assertion and audit metadata
packages/server/src/routes/tokens.ts
Accepted IDs appear in signed assertion metadata and audit records. When the field is omitted, the audit metadata records machineIdMissing: "true".
Tests and self-hosting examples
packages/server/src/__tests__/tokens-route.test.ts, packages/server/src/__tests__/selfhost-keys-issuer-bootstrap.test.ts, scripts/selfhost-smoke.sh, docs/self-hosting.md
Route tests cover valid, invalid, and omitted IDs, along with authorization and required-claim behavior. The bootstrap test and smoke request supply an ID. The guide documents the format and caller restrictions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AssertionRoute
  participant issueRelayhistoryAssertion
  participant createAssertionAuditEntry
  Caller->>AssertionRoute: Assertion request with optional machineId
  AssertionRoute->>issueRelayhistoryAssertion: Validated request
  issueRelayhistoryAssertion->>createAssertionAuditEntry: machineId for audit metadata
Loading

Possibly related PRs

Suggested reviewers: khaliqgant, miyaontherelay

Merge Risk: Minimal · up to 92a8d

The change adds optional machine binding to History assertions. No merge-blocking issue was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: signing canonical meta.machineId values on History assertions.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description directly explains the added machineId support, validation, audit behavior, authorization boundary, compatibility requirements, and test results. It is relevant to the chan…
Full details: Description check

Explanation

The pull request description directly explains the added machineId support, validation, audit behavior, authorization boundary, compatibility requirements, and test results. It is relevant to the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 6 functions across 4 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

A rabbit checks the token’s trail
A machine ID rides without fail
The signed claims hold what passed the gate
The audit marks an absent state
I nibble clover, pleased and bright

# Conflicts:
#	charts/relayauth/templates/deployment.yaml
#	docs/self-hosting.md
@khaliqgant

Copy link
Copy Markdown
Member

Takeover update for exact head 7b7899f60c26e8d208efa38bbece7de0ce4302a6:

  • merged current main non-force;
  • resolved the only two text conflicts additively, retaining both the machine-binding and session-directory Helm env/docs contracts;
  • adapted the new machine-binding tests to current main's stricter broker-scope rule by seeding the out-of-band plain broker key (production authority was not widened);
  • npm test: 16/16 workspace tasks, server 741/741, root follow-up 8/8;
  • npm run typecheck: 11/11;
  • focused token route suite: 236/236;
  • worktree clean.

Hosted exact-head CI and independent SOC2 review are pending. This PR remains draft. No image publish, deploy, or merge was performed.

@khaliqgant khaliqgant changed the title feat(tokens): sign a canonical meta.machineId on History assertions feat(tokens): sign canonical meta.machineId on History assertions Oct 31, 2026
@khaliqgant

Copy link
Copy Markdown
Member

Additional exact-head validation on 2dd88a12cf8f15d0efb6880111527e20722bdd6c: npx turbo build --force completed 9/9 tasks with 0 cache hits. The only emitted warning is the pre-existing Observer Turbo outputs warning; the build succeeded. The generated tracked packages/types/tsconfig.tsbuildinfo was restored, and the worktree is clean.

@khaliqgant

Copy link
Copy Markdown
Member

Hosted validation is now green on exact head 92a8da72d1a28bed7fbb61ca328e86e42ccb5ff2: Build & Test passed (build, typecheck, test), SDK Contract Check passed, and amd64 image build passed. The publish job was skipped. Independent reviewers verified the option-2 compensating-control links and scope limits in the PR body; the remaining cross-org broker trust is explicitly recorded as residual risk. No merge, publish, deploy, or live action was performed.

@khaliqgant
khaliqgant marked this pull request as ready for review October 31, 2026 07:47
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-31T07:48:02.471753Z 92a8da7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@greptile-apps

greptile-apps Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Adds optional machineId to History assertions and signs it as meta.machineId.

  • History assertions can carry the courier machine they are meant for.

No actionable issues found. AgentRelayBot explicitly acknowledges that the broker must establish machine identity, omitted IDs remain supported for non-machine paths, and RelayHistory Cloud does not yet enforce machine binding. Those stated limits are not reported as defects.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Broker request] --> B{Dedicated API key allowed?}
  B -- No --> C[Reject request]
  B -- Yes --> D{machineId supplied?}
  D -- Yes --> E{Valid exact string?}
  E -- No --> C
  E -- Yes --> F[Sign meta.machineId and audit the ID]
  D -- No --> G[Omit claim and audit machineIdMissing]
  F --> H[Return History assertion]
  G --> H
Loading

Reviews (1) · Last reviewed commit: "docs(tokens): clarify missing machine au..." · Reviewed by Greptile

@khaliqgant
khaliqgant merged commit 784582e into main Oct 31, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants