Raxx · internal docs

internal · gated

Handoff Agent Least-Privilege Identity Model

Status: Proposed Date: 2026-06-20 UTC Author: software-architect ADR: 0131-handoff-agent-least-privilege-identity Extends: vault-multi-agent-access-pattern.md / ADR-0130 Parent card: #3738


1. Context

ADR-0130 and its companion design doc established the per-agent Infisical machine-identity model for vault access: every agent class gets its own scoped, read-only, independently revocable identity rather than sharing the admin identity. That model was designed for the vault surface specifically.

A handoff agent is a more demanding threat model than an ephemeral local sub-agent. A handoff runs unattended — it is a Claude Cloud / scheduled CCR agent, a new-repo agent, or any other autonomous task that operates without an operator present. It touches not just vault but the full set of credential surfaces a coding or ops agent needs: GitHub write access, cloud provider APIs, MCP tool bindings, and potentially payment or broker APIs.

Today, in the absence of a handoff permission model:

The operator-stated invariant: "anything handed off to any agent — a Claude Cloud / scheduled CCR agent, a new repo's agent, any unattended task — must get its OWN minted, least-privilege, independently-revocable identity; NEVER the shared admin identity or blanket permissions."

This document designs the complete least-privilege handoff permission model across every credential surface.


2. Invariants

The following invariants apply to every handoff agent. They are non-negotiable extensions of the platform-level invariants.

# Invariant
H1 No handoff agent ever holds the admin identity or blanket-scope credentials. Every handoff identity set is the minimum required for its task.
H2 Every handoff identity is independently revocable. Revoking one handoff agent's access does not affect other agents or services.
H3 Every handoff identity is time-bound. Short TTL on tokens; credentials not valid indefinitely.
H4 Provisioning a scoped identity set is step 0 of every handoff dispatch. No task runs before its identity set is in place.
H5 Every handoff identity access is audited. Who read/wrote what, when, under which handoff task.
H6 No stored credentials in form that can be replayed (platform invariant I1). Handoff identity secrets live in env/secret stores injected at spawn time; never in committed files.
H7 No handoff agent receives live/prod financial credentials unless the task explicitly requires them and the operator confirms. Default is test/sandbox mode for every financial surface.
H8 Every credential surface the handoff touches has a kill-switch. Killing the handoff identity revokes access within one TTL cycle without requiring redeploy of shared infrastructure.

3. Credential-Surface Inventory

The following table enumerates every credential surface a handoff agent may need and what least-privilege looks like for each.

3.1 Full surface table

Surface Full / Admin shape (never for handoffs) Least-privilege handoff shape TTL / rotation Independent revocation mechanism
Infisical vault Admin machine identity — full project read/write/delete Per-handoff read-only identity scoped to a single secret path glob (e.g., /MooseQuest/handoff/<task-id>/) Access token: 15 min; client secret: Velvet rotation cycle Revoke the per-handoff machine identity in Infisical (<60 s). ADR-0130 §6.5.
GitHub — repo contents raxx-dev-bot GitHub App, Contents R/W, Workflows R/W — entire repo Fine-grained PAT or per-handoff GitHub App install scoped to: Contents R/W for the target branch only (not protected branches), Pull Requests W (create + update own PRs), Issues W (create + comment). No admin, no secrets:read, no Workflows write, no merge to protected branches without review. 1-hour installation token (GitHub App model) or expiring PAT (max 24h for handoffs) Revoke the fine-grained PAT or uninstall the per-handoff App install from the repo.
GitHub — Actions secrets Full org/repo secrets admin No access. Handoffs never read or write Actions secrets. N/A N/A — forbidden by design.
GitHub — branch protection Bypass protection (admin) No bypass. Handoffs must open PRs for review; they cannot push directly to main. N/A N/A — forbidden by design.
Heroku Full platform API key (all apps, scale, config, dynos) Task-scoped Heroku API key with scope limited to: config-vars read/set on one named app, OR deploy to one named pipeline stage. No dyno scale, no add-on admin, no billing, no collaborator management. Short-lived (Heroku does not support sub-hour token TTL natively; rotate per Velvet cycle at task end) Delete the task-scoped API key from Heroku account.
AWS IAM user (claude-infisical-bootstrap) with broad permissions Per-task IAM role assumed via STS AssumeRole with explicit policy: only the SSM Parameter Store paths the handoff needs (ssm:GetParameter on /raxx/<task>/) or the S3 prefix it needs. Session duration: max 1 hour. STS session token: 15 min – 1 hour Revoke the STS session by revoking the IAM role's trust policy or deleting the session. Per feedback_aws_workloads_use_ssm_not_vault.
Stripe Full live secret key (sk_live_...) — billing, refunds, customers Test key only by default (sk_test_...), read-only scope if test mode supports it. Live key only if operator explicitly approves and task is production billing work. Stripe keys are long-lived; isolate by using restricted keys (Stripe Restricted Keys feature: specify read/write per resource type). Delete the restricted key in Stripe dashboard. For live keys: rotate the key; the old key is dead immediately.
Cloudflare (WAF, DNS, Bot Management) Zone-level API token with all permissions (CF_DNS_EDIT, Bot Management write) Per-handoff CF API token with zone-scoped permissions limited to the exact resource and action needed (e.g., DNS read-only on one zone; WAF rule read-only). No Zone Settings edit, no Bot Management write, no Accounts admin. CF API tokens support expiry dates; set max 24h for handoff tokens. Delete the CF API token in the CF dashboard or via API.
MCP servers / tool bindings Ambient session MCP: Infisical MCP (full path), filesystem, shell Only the MCP tools the handoff needs. e.g., a PR-writing handoff needs GitHub tools and vault read; it does NOT need filesystem write outside its worktree, shell exec, or Stripe tools. MCP tool list is explicitly enumerated in session_context. Session-scoped (live only while the handoff session is active) Terminate the session. MCP bindings are not persistent.
Alpaca / broker credentials Operator's live Alpaca OAuth token No access by default. Paper credentials only if the handoff needs to test a trading flow. Live credentials require paper-first gate pass + explicit operator override. Per paper-first gating invariant. Revoke OAuth grant in Alpaca dashboard.
Apple IAP server keys Full StoreKit server key (production) Test StoreKit environment key only for handoffs that need IAP testing. Production key only for designated IAP notification handler task. Apple keys are long-lived; isolate by environment (sandbox vs production). Revoke key in Apple Developer portal.

4. The Provisioning Model

4.1 Step 0 is not optional

Every handoff dispatch sequence begins with Step 0: provision the scoped identity set for this specific handoff task. The handoff task does not start until Step 0 is confirmed complete. This is an enforced gate, not a suggestion.

4.2 Identity naming

Handoff identities follow a canonical naming convention so they can be found, audited, and revoked without searching:

<surface>-handoff-<task-slug>-<YYYYMMDD>

Examples: - infisical-handoff-iap-notif-handler-20260620 - gh-pat-handoff-stripe-checkout-founders-20260620 - stripe-restricted-handoff-founders-checkout-20260620 - cf-token-handoff-waf-read-20260620

The task-slug matches the GitHub issue slug for the handoff card. This links the credential directly back to the card so revocation is unambiguous.

4.3 Where scoped credentials live

Scoped handoff credentials are stored in Infisical at:

/MooseQuest/handoffs/<task-slug>/

Each secret in that path is named after the surface: - INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET - GH_HANDOFF_TOKEN - STRIPE_RESTRICTED_KEY - CF_HANDOFF_TOKEN - etc.

The handoff receives ONLY the secrets at its task-slug path. It cannot read /MooseQuest/raptor/, /MooseQuest/velvet/, or any other production service path.

4.4 Velvet integration

Handoff identity credentials that are time-bound but task-duration-spanning (e.g., a multi-day scheduled CCR run) are enrolled in the Velvet subscription manifest at the time the handoff is provisioned, under a handoff consumer class:

# velvet subscription entry for a handoff
- token_name: STRIPE_RESTRICTED_KEY
  consumer_id: handoff-founders-checkout-20260620
  env: prod
  active: true
  update_endpoint: "velvet://infisical/handoffs/founders-checkout-20260620/STRIPE_RESTRICTED_KEY"
  update_method: INFISICAL_WRITE
  update_auth_token_name: null
  healthcheck_endpoint: null
  required: false
  capabilities:
    - update
  description: "Handoff task: founders-checkout-20260620 — Stripe restricted test key"
  auto_revoke_after: "2026-06-21T00:00:00Z"   # task-specific TTL (new Velvet field)

The auto_revoke_after field (new Velvet capability — see sub-card SC-HANDOFF-VELVET-01) triggers an automatic revocation of the credential and removal of the manifest entry when the task TTL expires. No manual cleanup required.

For short-lived handoffs (single CI run, one-shot task < 1 hour), Velvet enrollment is not required — the credential TTL is set at the surface level and the credential is manually deleted on task completion.

4.5 Session context injection

For Claude Cloud / scheduled CCR agents, the scoped credential set is injected into the agent's session_context (or equivalent env block) at spawn time:

{
  "env": {
    "INFISICAL_CLIENT_ID": "<handoff-specific-client-id>",
    "INFISICAL_CLIENT_SECRET": "<handoff-specific-client-secret>",
    "INFISICAL_SECRET_PATH": "/MooseQuest/handoffs/<task-slug>/",
    "GH_TOKEN": "<scoped-installation-token-or-pat>",
    "STRIPE_KEY": "<restricted-test-key>",
    "HANDOFF_TASK_SLUG": "<task-slug>",
    "HANDOFF_EXPIRES_AT": "<ISO-8601-UTC>"
  },
  "mcp_tools": ["github_pr_create", "github_pr_comment", "infisical_get_secret"],
  "mcp_tools_excluded": ["filesystem_write_outside_worktree", "stripe_charge", "heroku_scale"]
}

The explicit mcp_tools list is the tool allowlist. Anything not on the list is not available to the handoff session. The orchestrator enforces this at spawn time via the agent_token_injector extension (sub-card SC-HANDOFF-SESSIONCTX-01).

4.6 Independent revocation

To revoke any handoff agent's access:

  1. For Infisical: delete the machine identity infisical-handoff-<task-slug>-<date> in Infisical admin panel or via API. Takes effect within one token TTL cycle (15 min max).
  2. For GitHub: delete the fine-grained PAT or revoke the GitHub App install for the handoff. Immediate effect.
  3. For Stripe: delete the restricted key. Immediate effect.
  4. For Cloudflare: delete the API token. Immediate effect.
  5. For AWS: revoke the STS session or delete the task-specific IAM role assumption. Takes effect within the session token TTL (max 1 hour).
  6. For Heroku: delete the task-scoped API key. Immediate effect.

None of these revocations affect any other agent or service. Each surface credential is independent of all others.


5. GitHub Scoping for Handoff Agents

The existing GitHub identity model (ADR-0128, agent-github-identity.md) uses three shared GitHub Apps (raxx-dev-bot, raxx-ops-bot, raxx-pm-bot) with repo-level permissions granted to the entire agent class. This is appropriate for live, operator-present sessions but too broad for unattended handoffs.

5.1 The least-privilege GitHub shape for a handoff

A handoff agent needs: - contents:write — to create commits and branches in its worktree - pull_requests:write — to open and update its own PR - issues:write — to create issue comments reporting results

A handoff agent must NOT have: - Admin permissions (branch protection override, repo settings) - secrets:read or secrets:write — Actions secrets are off-limits - workflows:write — handoffs do not modify CI definitions - The ability to push directly to main, staging, or any other protected branch - The ability to merge without review (cannot approve its own PR)

5.2 Implementation options

Option A — Fine-grained PAT (simplest) Create a fine-grained PAT under a bot account scoped to the specific repo, with exactly the three permissions listed above, expiring in 24 hours. Store in Infisical at /MooseQuest/handoffs/<task-slug>/GH_HANDOFF_TOKEN. The orchestrator reads this at spawn time and injects it as GH_TOKEN.

Limitation: Fine-grained PATs require a user account or organization-owned bot account. Until a dedicated per-handoff bot account exists, raxx-dev-bot can generate a fine-grained PAT via the GitHub API under its own account, but this does not provide stronger repo isolation than the existing installation token.

Option B — Per-handoff GitHub App install scope (preferred for multi-task) When the "GitHub connected to repo" prerequisite is met (see §7), the GitHub App install can be scoped at install time to a specific repo with specific permissions. For handoffs launched from multiple repos, each repo gets its own scoped App install. This is the correct steady-state model and what the sub-card SC-HANDOFF-GITHUB-01 implements.

Recommendation: Option A for the immediate near-term (until App scoping is wired). Option B as the target state. The choice does not affect the security invariants — both options prohibit protected-branch push and admin permissions.

5.3 Branch protection as a backstop

Even if a handoff agent's GitHub token were broader than intended, branch protection on main and staging provides a hard backstop: direct pushes to those branches are rejected by GitHub regardless of token scope. The handoff can only open a PR, which requires human review before merge.


6. Blast-Radius Invariant and Enforcement Gate

6.1 The invariant (formal statement)

HANDOFF-INVARIANT-1: No handoff agent ever holds the admin identity or any blanket-scope credential. Every handoff agent's access is: (a) least-privilege — minimum permissions to complete the task; (b) scoped — bounded to the task's specific resources, not the whole platform; (c) time-bound — credentials expire within the handoff's expected runtime or a fixed maximum (24h); (d) independently revocable — removing one handoff agent's access has no effect on other agents or services; (e) audited — every access event is logged with the handoff task slug.

6.2 The handoff provisioning gate (checklist)

This checklist is mandatory before any handoff task is dispatched. It is the operator-or-orchestrator's responsibility to confirm each item.

HANDOFF PROVISIONING GATE — must complete before task dispatch

Surface checklist:
[ ] Infisical: per-handoff read-only identity provisioned at /MooseQuest/handoffs/<task-slug>/
    Scope: only the secret paths this task needs. No admin project scope. TTL: 15-min access tokens.
[ ] GitHub: fine-grained PAT or scoped App install provisioned.
    Permissions: contents:write, pull_requests:write, issues:write — nothing else.
    Expiry: 24h max. Protected-branch push disabled.
[ ] Cloud/CI creds: task-specific, test/sandbox by default.
    If prod creds needed: operator explicit confirmation required (mark here: ___).
    AWS: STS AssumeRole with narrowly-scoped session policy. Max 1-hour session.
    Heroku: task-scoped key, single app, no scale/admin/billing.
    Stripe: restricted test key unless operator approves live key.
    Cloudflare: scoped token with expiry. No Zone Settings write, no Bot Management write.
[ ] MCP tools: explicit allowlist defined. Excluded tools listed.
[ ] session_context / env: only the above scoped credentials injected. No admin env vars.
[ ] Velvet enrollment: if task duration > 1 hour, enrolled in subscription-manifest.yml.
    If task duration < 1 hour: credential will be manually deleted on task completion.
[ ] Revocation path documented: for each surface, who deletes what and where.
[ ] Audit path confirmed: credential access logs will be retained per platform retention policy (90 days).

Task context:
[ ] Task slug: ______________________
[ ] Expected runtime: _______________
[ ] Handoff expires at (UTC): _______
[ ] Operator who approved: __________
[ ] Linked card: #__________________

6.3 Enforcement mechanism

The gate is currently a manual checklist. The sub-card SC-HANDOFF-GATE-01 implements an automated gate: a script (scripts/agents/provision_handoff_identity.py) that: 1. Reads a handoff spec file (handoff-spec.json) defining task slug, surfaces needed, and TTL. 2. Provisions each surface's scoped credential in sequence (or surfaces an error if provisioning fails). 3. Writes a confirmation manifest (/MooseQuest/handoffs/<task-slug>/PROVISION_MANIFEST) to vault. 4. Returns a structured env block for the orchestrator to inject at spawn time.

The orchestrator blocks dispatch unless it can read the PROVISION_MANIFEST for the task.


7. GitHub Connection Prerequisite for Cloud Agents

Cloud-hosted CCR agents that need GitHub access require the GitHub App to be installed on the repo with the agent's permitted scope. This is not a one-time setup — it is a per-handoff configuration concern.

Recommendation: - The raxx-dev-bot GitHub App installation on the repo should be configured with the minimum permissions the handoff class needs (contents:write, pull_requests:write, issues:write — matching §5.1). - For cloud agents operating on separate repos, a new App install is required per repo. The App should NOT be installed with org-wide permissions. - The GitHub App's Web URL setup (/web-setup) should be performed once per repo where a cloud agent will operate. This doubles as the per-agent GitHub boundary: the agent can only act on repos where the App is installed. - App installation with excess permissions (e.g., admin, secrets) is a violation of HANDOFF-INVARIANT-1 and must be caught by the provisioning gate.


8. Handoff Candidates: #3629 and #3206

8.1 #3629 — Apple IAP server-side notification handler

Task scope: Implement the StoreKit 2 server-to-server notification endpoint that receives Apple's purchase/subscription/refund events and reconciles them against Queue's billing state.

Required scoped identity set for a cloud handoff:

Surface Scoped shape
Infisical vault Read-only identity scoped to /MooseQuest/handoffs/iap-notif-handler-<date>/ and /MooseQuest/queue/staging/APPLE_IAP_* (staging keys only). No prod billing path.
GitHub fine-grained PAT: contents:write on TradeMasterAPI, pull_requests:write. Expiry: 24h.
Apple IAP key Sandbox StoreKit server key ONLY. No production key. The endpoint must be implemented and tested in Apple's Sandbox environment before any production key is issued.
Stripe None. IAP billing is Apple IAP, not Stripe.
AWS None unless the notification endpoint involves SQS/SNS email; if so: STS session, sqs:SendMessage on one specific queue ARN.
Heroku None for implementation. If integration test requires staging deploy: config-vars read on raxx-api-staging only.
MCP tools GitHub PR tools, Infisical read. No filesystem write outside worktree, no shell exec of deployment commands.

Prod-key gate: Apple production IAP key must NOT be provided to this handoff. A separate operator-approved handoff is required for the production enablement after staging validation. This enforces the paper-first gating invariant adapted to IAP: "sandbox-first, prod key only after sandbox profitable / stable."

8.2 #3206 — Founders Stripe Checkout

Task scope: Implement the Founders promotional pricing Stripe Checkout flow — create sessions, handle webhook events for subscription activation, reconcile with the founders trial engine.

Required scoped identity set for a cloud handoff:

Surface Scoped shape
Infisical vault Read-only identity scoped to /MooseQuest/handoffs/founders-checkout-<date>/ and /MooseQuest/queue/staging/STRIPE_* (test keys only). No prod Stripe path.
GitHub fine-grained PAT: contents:write on TradeMasterAPI, pull_requests:write. Expiry: 24h.
Stripe Restricted TEST key (sk_test_...) with permissions: checkout_sessions:write, subscriptions:read, webhook_endpoints:read. No live key. No customer PII write. No refund capability.
AWS None unless the Stripe webhook flow uses SQS DLQ; if so: STS session scoped to that queue ARN.
Heroku None for implementation. If staging deploy is needed: config-vars read on raxx-api-staging only.
MCP tools GitHub PR tools, Infisical read. No live Stripe charge tool. No Heroku scale/admin tool.

Live-key gate: Stripe live key (sk_live_...) is explicitly withheld from this handoff. The task is: implement and test in Stripe test mode. A separate operator-approved handoff is required for live-key provisioning after the test-mode flow is reviewed and merged. This enforces HANDOFF-INVARIANT-1 H7 and the platform's no-live-financial-creds-without-approval rule.


9. Sequence: Handoff Provisioning + Dispatch

sequenceDiagram
    actor OP as Operator
    participant GATE as Provisioning Script (provision_handoff_identity.py)
    participant IS as Infisical Admin
    participant GH as GitHub (fine-grained PAT API)
    participant VLT as Vault (/MooseQuest/handoffs/<slug>/)
    participant ORCH as Orchestrator / CCR dispatch
    participant AGNT as Handoff Agent (cloud / CCR)

    OP->>GATE: provision_handoff_identity.py --spec handoff-spec.json
    GATE->>GATE: validate spec (task-slug, surfaces, TTL, operator-approval)
    GATE->>IS: create machine identity infisical-handoff-<slug>-<date>
    IS-->>GATE: { client_id, client_secret }
    GATE->>VLT: write client_id + client_secret to /MooseQuest/handoffs/<slug>/
    GATE->>GH: create fine-grained PAT for task (24h expiry, scoped permissions)
    GH-->>GATE: { token: "github_pat_..." }
    GATE->>VLT: write GH_HANDOFF_TOKEN to /MooseQuest/handoffs/<slug>/
    Note over GATE: Repeat for each surface in spec (Stripe, CF, AWS STS, etc.)
    GATE->>VLT: write PROVISION_MANIFEST (confirmed, timestamp, surfaces, TTLs)
    GATE-->>OP: provisioning complete — env block ready

    OP->>ORCH: dispatch handoff agent for task <slug>
    ORCH->>VLT: read PROVISION_MANIFEST (confirm provisioned)
    ORCH->>VLT: read env block from /MooseQuest/handoffs/<slug>/
    ORCH->>AGNT: spawn with env={scoped creds only}, mcp_tools={allowlist}
    AGNT->>AGNT: performs task with scoped credentials only

    Note over AGNT: Task completes (or expires)
    AGNT->>ORCH: task result

    OP->>GATE: provision_handoff_identity.py --revoke --slug <slug>
    GATE->>IS: delete machine identity infisical-handoff-<slug>-<date>
    GATE->>GH: delete fine-grained PAT
    GATE->>VLT: delete /MooseQuest/handoffs/<slug>/ (all secrets)
    Note over GATE: All handoff credentials revoked; no residue

10. Migrations

No application schema changes. No new Postgres tables.

Infisical config changes: - New vault path /MooseQuest/handoffs/ (folder must be created before first use per feedback_vault_folder_must_exist). - New machine identity per handoff (provisioned and revoked per task).

Velvet subscription manifest changes: - New auto_revoke_after field on handoff consumer entries (backward-compatible; non-handoff entries are unaffected).

GitHub App changes: - No changes to existing App installations. - Per-handoff fine-grained PATs are created on demand and deleted on task completion.

Rollback: If the provisioning script fails, no handoff credentials are in play. The handoff is not dispatched. Revocation of a partially-provisioned set is surfaced by the script — each surface is checked for provisioning state before write.


11. Rollout Plan

dark (now)     Handoff provisioning checklist (manual) enforced for all new handoff dispatches.
               Document the gate in runbook: docs/ops/runbooks/handoff-provisioning.md.
               Apply retroactively to #3629 and #3206 before dispatch.

flag           provision_handoff_identity.py script ships (SC-HANDOFF-GATE-01).
               Orchestrator reads PROVISION_MANIFEST before dispatch (SC-HANDOFF-SESSIONCTX-01).
               Manual gate is the fallback until script is proven.

beta           Velvet auto_revoke_after field implemented (SC-HANDOFF-VELVET-01).
               All handoff credentials with duration > 1 hour auto-enrolled in Velvet.

ga             GitHub App per-handoff scoping (SC-HANDOFF-GITHUB-01) replaces fine-grained PATs.
               Full automated provisioning + revocation with no manual steps.

12. Security Considerations

PII collected: Handoff audit logs record the machine identity (task slug), secret path, and timestamp. No credential values are logged. If the handoff processes user data (e.g., IAP notification contains Apple subscription UUID correlated to a Raxx user), that data is handled by the handoff's task logic — it is not part of the identity provisioning log. Retention: 90 days, matching platform policy.

Deletion on DSR: Handoff access logs do not contain end-user PII by default. If a handoff processes user-correlated events, the task-level data is in Queue's billing tables (DSR path: Queue's data export / erasure endpoint). Handoff identity audit logs are not user-correlated.

Stored credentials: Scoped credentials live in Infisical at /MooseQuest/handoffs/<slug>/ and in the spawned agent's env. They are not committed to git, not written to disk, not included in logs. Satisfies invariant H6 and platform invariant I1.

Breach notification: If a handoff identity is compromised, blast radius is limited to the task's scoped paths and surfaces. Discovery: Infisical audit log shows reads from unexpected IP/time. Response: revoke the per-handoff identity (<60 seconds); the handoff's scoped credentials have no access beyond the task's resources. If the compromised credential had access to user-correlated data (e.g., Stripe test customer IDs): GDPR 72-hour notification clock starts on confirmation of compromise. Test/sandbox credentials (the default) do not expose live customer data.

Does any part of this store a credential that can be replayed? Handoff credentials are stored in Infisical (environment variable injection at spawn). They are time-bound (TTL enforced). After task expiry or revocation, the credential is no longer valid. No replay is possible beyond the TTL window. This satisfies H3 and H6.

Kill-switch: Revoke the per-handoff Infisical machine identity. All other surfaces (GitHub PAT, Stripe restricted key, CF token) are independently revocable. The Velvet auto_revoke_after field automates expiry without operator action.

Live financial credentials: HANDOFF-INVARIANT-1 H7 requires explicit operator confirmation before a live financial key (Stripe live, Apple production, Alpaca live) is issued to any handoff. This confirmation must be recorded in the handoff's provisioning manifest. Test/sandbox is the default and requires no special approval.

Kill-switch for live execution paths: The paper-first gating platform invariant applies to any handoff that touches trading execution paths. No handoff receives live broker credentials without a paper-profitable-for-N-cycles gate pass or explicit per-flow operator override.


13. Open Questions

# Question Blocks Urgency
OQ1 Fine-grained PAT vs per-handoff GitHub App install. GitHub's fine-grained PAT requires a user/bot account that owns the PAT. Until SC-HANDOFF-GITHUB-01 wires up per-install App scoping, which bot account do fine-grained handoff PATs live under? Recommendation: raxx-dev-bot account, but this still grants the PAT the bot's account identity. Does the operator want to accept this for the interim, or block on App install scoping? SC-HANDOFF-GITHUB-01 (shape) Blocks first handoff dispatch
OQ2 Heroku task-scoped API key shape. Heroku does not natively support per-resource-scoped API keys (keys are account-scoped). The workaround is a dedicated sub-account per task or per-app OAuth token. Which does the operator prefer, or is Heroku excluded from handoff self-provisioning (operator action required for any Heroku credential)? SC-HANDOFF-GATE-01 Should resolve before handoff spec for tasks needing Heroku access
OQ3 auto_revoke_after Velvet field. Is the operator ready to extend the Velvet manifest schema with auto_revoke_after? This is a new optional field; backward-compatible, but needs Velvet code change and PR. Should this be part of SC-HANDOFF-VELVET-01 or a separate Velvet maintenance card? SC-HANDOFF-VELVET-01 Does not block initial handoffs (short-lived tasks can use manual revocation)
OQ4 Apple IAP production key handoff gate. After #3629 sandbox implementation is reviewed + merged, what is the approval signal required to issue the production StoreKit key to a handoff? Operator explicit confirmation via issue comment? Or does production IAP key issuance always require a human (operator) in the loop and cannot be scripted? #3629 prod enablement Does not block #3629 sandbox handoff