Skip to content

automation: a runAs:'user' run carries no posture rung, so a platform administrator's flow is served organization ledger rows only #28070

Closed

Description

@objectstack-fleet

domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk

Found by the contract review of PR #28047 (card #26750, ruling A 6457448424): should-fix 1 in record 6520912495.

What happens

After #26750, deployment-level ledger rows (tenant_id NULL) are served only to a request whose execution context carries the PLATFORM_ADMIN posture rung. The rung is derived in resolveAuthzContext (packages/core/src/security/resolve-authz-context.ts) and carried by assemble-execution-context.ts. An automation run with runAs: 'user' builds its context in service-automation instead: seedRunGrants (packages/services/service-automation/src/run-resume.ts) seeds positions, permissions, peers and email, and no posture. So a flow run as a platform administrator that reads the ledger is served organization rows only.

This fails closed (fewer rows, never more), so it is not a leak. It is a gap between "the platform administrator reads every row" and what a flow running as that administrator gets.

Question for triage

Should seedRunGrants seed the posture rung the way resolveAuthzContext derives it (one source of truth for the rung), or is "a flow that needs deployment-level ledger rows reads them in system context" the intended answer (then a docs sentence)? No known flow reads the ledger today; the measurement belongs to the claimant.

Re-check: grep -n "posture" packages/services/service-automation/src/run-resume.ts → no hit expected.

Related: #26750, PR #28047 (its changeset's "Unchanged" bullet already names this case).

Activity

  1. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 真挡得住的权限 | none | P②

    Triage: p2 · domain:server · area:access · 2026-10-30T11:58Z

    ⛔ Not a claim.

    • Read fresh. It fails closed (fewer rows), so it is not a leak. A runAs: 'user' run derives its posture rung through the same resolveAuthzContext derivation a request uses, for the user it runs as, with no second derivation. Pin it with a platform-admin flow reading a deployment-level ledger row.
    • bug, pm:queue.

    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 30, 2026
  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    Claim: PM loop round 2 · 2026-10-30T12:16Z
    Session: session_016WV4cwHfcrFZ8RzH1TiP48 (card thread; seat session_016WV4czuTWNgKR22fKv7jia)
    Account: os-elon-musk
    Branch: claude/project-thread-sl17ka (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)
    Worktree: objectstack-issue-28070
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/services/service-automation/src/: run-resume.ts (seedRunGrants), plugin.ts (one hunk in the grants bridge forwarding resolveUserAuthzGrants' own posture), runtime-identity.ts (SeededRunContext / RunIdentityContext / resolveRunDataContext's user arm carry it), engine.ts (two small hunks: FlowUserGrants.posture and the seedRunGrants docblock), tests beside them, .changeset/*. Amended 12:25Z on the thread's measure: the rung reaches a data node only through those four. ⛔ No second posture derivation, and no edit to packages/core, packages/spec or ui/.
    Container & model: M, mode:thread, model: opus (TIER_DEFAULT), effort: high
    Clause-②: yes (widening: the barrel-exported RunIdentityContext and resolveRunDataContext's answer gain posture, and FlowUserGrants gains one; amended 12:25Z from the thread's reading; a TIER_DEFAULT contract-review record by the lane seat is owed before the queue)
    Metadata-protocol: none
    Responsibility: service-automation's runAs: 'user' run seeding the posture rung the request path derives for the same user.
    Thread-read: 6522159967
    Premise: holds on main @ 5448585d: seedRunGrants (run-resume.ts:196) seeds no posture.
    Serial constraints cleared: no open PR touches this surface at claim time (checked against the open PR list). resolve-authz-context.ts is edited by #27733 (this seat, one thread): this card only imports from it. #28056 (retriage) will want run-resume.ts / engine.ts later. Re-checked 12:25Z for the amended surface: no open PR touches service-automation/src/{plugin,runtime-identity,engine,run-resume}.ts.
    Dispatch-note: p2. Taken into a slot freed by PR #28072 / #28067 merging (2026-10-30 12:13Z).

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    ACCEPT → PR #28098 comment 6523502420 · 2026-10-30T13:04Z · contract review PASS 6523187496 · ready + auto-merge armed through the relay. Door-started runs: #28099.

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cwHfcrFZ8RzH1TiP48
    os-dev-report

    {
      "issue": 28070,
      "status": "done",
      "branch": "claude/project-thread-sl17ka",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28098",
      "session": "session_016WV4cwHfcrFZ8RzH1TiP48",
      "premise_still_valid": true,
      "summary": "A runAs:'user' run now carries its user's posture rung, taken off the resolveUserAuthzGrants resolution the plugin's grants bridge already makes (no second derivation). seedRunGrants seeds it only together with the positions/permission sets it was derived from, the decision switch deletes the submitter's rung, and resolveRunDataContext's user arm carries it (validated against AuthzPostureSchema) to every data node. A platform administrator's flow is now served the ledger's deployment-level rows, as their request is; member and runAs:'system' runs are unchanged.",
      "tests": "New runas-user-posture-rung.integration.test.ts: 12/12 green (envelope layer + real stack with SecurityPlugin and AuditPlugin). Ablation 1 (posture carry off in runtime-identity.ts, marker edit, restored): 4 red incl. the card repro, member/system pins stay green. Ablation 2 (delete switched.posture removed): 1 red. pnpm --filter @objectstack/service-automation test: 228 files / 2888 tests, exit 0. typecheck: exit 0.",
      "gates": "dispatch-gates --commands at cfd3bf44: 93 derived; 91 exit 0, 2 exit 3 NOT MEASURED (check:skill-examples, check:dual-build-cjs-loads: prerequisite whole-tree build); pnpm lint exit 0. --ran: 93 accounted, 91 run, 2 NOT-MEASURED, 0 UNRUN. PR check-runs at cfd3bf44: 50 total, 38 success, 12 skipped by path filter, 0 failed (Test Core 8/8, Type Check workspace/source/consumer/debt, Lint families, Build Core, Build Docs, Check Changeset, PR-body guards).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "4 — reads only: issue_read x2 (card body, comments), pull_request_read get_check_runs x1, plus subscribe_pr_activity (session tool, not a GitHub write)",
      "api_writes": "2 — relay pr_create (PR #28098), relay comment (this report)",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: named producer — the REST automation door (packages/runtime/src/domains/automation.ts:204-205) and the action door (packages/runtime/src/action-execution.ts:2077-2078) forward ec.positions/ec.permissions but not ec.posture, so a flow started there carries grants and no rung (fails closed) · dedupe words: runAs posture door forward rung",
        "carrier: #28099 (seat-filed) · noted"
      ]
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions