Skip to content

CI-ONLY: do not merge, exercises #117 @ cd6ffb98a6d29bd8df6521204d91c0dc7dc1a9fb - #118

Closed
AgentRelayBot wants to merge 4 commits into
mainfrom
feat/migration-plan-v2-prototype
Closed

AgentRelayBot wants to merge 4 commits into
mainfrom
feat/migration-plan-v2-prototype

Conversation

@AgentRelayBot

@AgentRelayBot AgentRelayBot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

CI-ONLY. Do not merge. This exists only to run hosted CI at the exact head of #117: feat/migration-plan-v2-prototype @ cd6ffb98a6d29bd8df6521204d91c0dc7dc1a9fb.

#117 is stacked on #111, and relayauth's ci.yml only triggers for PRs targeting main and has no workflow_dispatch. No workflow file was edited and no branch guard was changed. Because this PR targets main, its diff also includes the #110 and #111 stack commits.

This PR will be closed once CI reports. Review and merge happen on #117 and its stack.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

View guided diff


Agent Relay sessions

  • claude session 719b5435 · opened via gh pr create · last active 2026-10-31

kjgbot and others added 4 commits October 30, 2026 13:23
Adds a narrow path for a one-time history export to mint short-lived
assertions under each original user's identity, instead of reusing the
relayhistory assertion key (which mints for any orgId/sponsorId in the body).

- 0012_migration_grants.sql: migration_grants table (plan digest, single
  audience, bounded allow-list, not_after, revoked_at; no cascades, no
  retention), plus an (org_id, api_key_id) index.
- MigrationGrantStorage (create/get/revoke, each atomic with its audit
  entry) on SQLite and the in-memory backend; optional on AuthStorage so
  external adapters can adopt it incrementally (routes answer 501 without it).
- POST /v1/migration-grants and POST /v1/migration-grants/:grantId/revoke,
  scope relayauth:api-key:manage:*. The bound key must be an unrevoked
  operator api key of the caller's org whose only scope is
  relayauth:assertion:create:migration. Every allowed user must be an active
  human RelayAuth identity of the org (fail closed, no allow-list fallback).
- POST /v1/tokens/migration-assertion: dedicated api key only, grant must
  name that key, same org, unrevoked, before not_after, user listed and
  still an active human of the org. 1-60s TTL capped at not_after, scopes
  exactly ["rth:sync"], aud = grant audience, no refresh token.
- Audit actions migration_grant.created / migration_grant.revoked; every
  refusal after authentication is audited (result denied) in the caller's org.
- docs/self-hosting.md: contract and threat model. The relayhistory
  assertion endpoint is unchanged (its origin normaliser is now exported).

Review: adversarial pass (scope confusion between the two assertion
scopes, wildcard/bearer/workspace-token callers, cross-org grant and key
use, identity changes after grant, audit atomicity, denial-audit failure
handling) and contextual pass (spec, coordinator amendments, repo
conventions) on the final diff.
Validation (Node 22.14): turbo build, turbo typecheck, turbo test --force
(server 733/733 incl. 47 new), root tests 8/8, contract surface check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, commit identity)

PROTOTYPE ONLY. Built against 96BullSessions #86 @ e74be4c0, which is on
contract-review HOLD pending amendments. Not final; do not merge.

- Plan: optional `backfills` of v2 inputs {v:2, kind:"backfill", key,
  org_id, workspace_id, source_user_id, session, format:"ndjson",
  chunks:[{start_offset, bytes, sha256, records}]}. The issuer adds
  grant_id and derives backfill_sha256 (v1 hash chain, epoch 0) and
  raw_bytes; client-supplied derived fields are refused. records is
  mandatory, offsets contiguous from 0, keys unique across both lists,
  pairs and limits span both lists.
- Digest: absent and empty `backfills` give the byte-identical v1 plan
  and digest. With a backfill: sha256(canonical({v:2, grant_id,
  exported_at, manifests, backfills})). verifyStoredMigrationGrantPlan
  recomputes both shapes; a v2 row with no backfills or a v1 row
  carrying backfills is refused. Old binaries refuse v2 rows (fail
  closed).
- Mint: upload is exactly {key} (v1, unchanged), {key, kind:
  "backfill_base_read"} or {key, base}; each form looks up only its own
  list; tenancy and digests come only from the stored entry.
- Base-read capability follows the review direction: its own subject,
  audience (<grant origin>/v1/pipeline/manifests/base), scope
  (rth:migration-base:read), tokenClass, TTL (<=30 s) and jti namespace
  (mbr_), audited as resource migration-base-read. Single-use consume
  is the ingest's job.
- Base: exact 7 keys and safe-integer bounds (revision 1..2^53-2,
  chunk_count <= 2^53-1-4096, epoch <= 2^32-2), strings 1..512, owner
  not migration:*, content_sha256 lowercase hex.
- Audit: upload_kind, entry_sha256 and base_revision only.
- SDK verifier and @relayauth/types accept exactly the three shapes.

Tests: vendored #79 @ c91f4ac0 vectors reproduced (entry, backfill and
plan digests, commit identity, work key); v1 digest a6682d4d unchanged
for absent and []; negatives from #86 section 8; stored-plan tamper;
base bounds; mint form/list/pair/selector matrix with audited refusals;
verifier audience matrix both ways.

Validation (local): turbo typecheck 11/11, build 9/9, npm test 16/16
(server 805/805, sdk 171/171, ai 70, core 19, migrate 18, relayauth 12,
types 1, root 8); server e2e 40 pass, 93 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aligns the #117 prototype with 96BullSessions #86, the spec of record
(main @ f67f741; reviewed and PASSED at d4e0e040, same blobs):
docs/contracts/relayhistory-export-plan-entry-v2-issuer.md and its
vectors (blob 59a8bde).

- Base-read capability: sub agent_relayhistory_migration_base_read,
  tokenClass migration_base_read, scope rth:migration_base_read, aud
  <origin>/v1/pipeline/manifests/base, TTL <= 30 s, jti mbr_, identity
  in meta.read with meta.upload absent. Issuance is default-off behind
  RELAYAUTH_ENABLE_MIGRATION_BASE_READ (403 migration_base_read_disabled;
  chart migrationAssertions.baseReadEnabled).
- Legacy conditional (section 1): absent or [] backfills is the v1 path
  byte for byte, including v1's 1..1000 manifests rule; a non-empty
  list is v2 and may have manifests: [] (ruling 2: >= 1 combined entry);
  null or non-array backfills is invalid_plan.
- Exact bounds (section 2): start_offset + bytes <= MAX checked without
  a float sum; records mandatory 0..MAX.
- Rulings: v1 mint and refusal audit rows unchanged (no upload_kind or
  entry_sha256 on v1); v2 rows omit the raw nonce and correlate by
  tokenId; v2 wrong-org is a generic invalid_plan externally with
  detail backfill_org_mismatch only in the internal audit; v1 keeps
  plan_org_mismatch.
- SDK verifier: meta.upload is v1 or v2 commit only; meta.read is the
  base-read only; both together are refused.
- Tests use #86's issuer vectors (legacy.absent, legacy.empty, v2_plan,
  backfill_entry_*, base_read_meta_read, commit_meta_upload) in place of
  the #79 c91f4ac0 copy.

Review: adversarial (token-class confusion both ways at the verifier,
selector/list/pair/tenant override refusals, bounds at MAX and MAX + 1,
flag exactness, no raw key/nonce/base content in audit) and contextual
(v1 vectors and #111 tests unchanged, docs, chart, env wiring). Fixed a
timestamp-ordering flake in a new test found by repeated runs.

Validation (local; hosted CI only triggers for base main):
turbo typecheck --force 11/11, build --force 9/9, test --force 16/16
(0 cached): server 816/816, sdk 171/171, ai 70, core 19, migrate 18,
relayauth 12, types 1; root tests 8/8; server e2e 40 pass, 93 skipped.
prettier 3.6.2 --no-config --check: new files pass; the warned files
are exactly those already non-conforming at 2adf414.

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

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • 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

@AgentRelayBot

Copy link
Copy Markdown
Contributor Author

CI reported at #117 head cd6ffb9: CI (Build & Test) success https://github.com/AgentWorkforce/relayauth/actions/runs/41168349534, SDK Contract Check success https://github.com/AgentWorkforce/relayauth/actions/runs/41168349538, Container Image (build amd64) success / publish skipped https://github.com/AgentWorkforce/relayauth/actions/runs/41168349535. Closing as planned; the branch is #117's and is not deleted.

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