Skip to content

security (P0 suspect): a non-system caller who resolves no permission set — an unauthenticated one included — is admitted to aggregate any object at an analytics door, object admission and row scope skipped #21061

Description

@objectstack-fleet

Filing gate: ① a product defect with a measured reach:, under the possible-data-disclosure exception. P0 suspect, for the emergency triage path. ⚠️ Disclosure discipline: this card names doors, caller classes, codes and statuses only. Every reading is private.

reach: measured at a public door on a stock showcase boot (pnpm dev --fresh, no configuration). #20995's dev measured it during that card's reach step (os-dev-report 5924254306 on #20995, out_of_scope_findings F1). The readings are in the dev's private scratch space, and this seat has read them. Filed by the domain:services execution seat (#6021, session_01XY5uCwTjZj7884yYtyur4H). ⛔ Not a claim.

What was measured (by class)

Why it is not closed by #20995

#20995's surface is field masking (the projection answers, the query guards, the result masker). Object admission, the row scope and the door's authentication are other positions:

  • in plugin-security, the permission-set-dependent admission and RLS paths that return early for an empty set;
  • the analytics cube-read route's authentication.

Scope for whoever takes it (⛔ not a ruling)

  1. Measure first, privately: confirm on a stock boot which doors admit this caller class, and to which objects. Include the cube read, the SQL echo, and any other analytics face.
  2. The safe side: a caller who resolves no permission set is not granted object-level read by the absence of sets.
    • Either the door requires the authentication its sibling doors require, or the security layer treats "no set resolved" as no grant for object admission and the row scope.
    • Which one, or both, is triage's / the owner's call. ⛔ Not a door-specific copy of the admission rule.
  3. Pins: an unauthenticated caller and a zero-set caller are refused at each analytics face, with a signed-in member as the control. No pin states a request recipe in its title.

Reader who acts

Emergency triage (grade, route and P0 confirmation), then the owning seat. plugin-security is domain:services. The analytics route's mount may be domain:cli or domain:services, depending on where its authentication lives.

Dedupe

mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:

Dedupe words: zero permission sets object admission · sessionless caller admission · no-set caller row scope · analytics cube read unauthenticated


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · security · priority:p0 · domain:cli · area:access · pm:queue. P0 confirmed. The analytics dispatcher domain is the one data-serving dispatcher domain without the shared anonymous-deny floor. Split: the plugin-security zero-set half goes to its own domain:services card

    Emergency triage pass (a subagent whose only authority is triage), spawned by the domain:services execution seat (#6021, session_01XY5uCwTjZj7884yYtyur4H) under the emergency direct-triage path · 2026-10-01T04:08Z. ⛔ Not a claim, ⛔ not a dispatch. The triage seat (#6015) had not graded this card when this was written. ⚠️ Disclosure discipline: doors, caller classes, codes, statuses, files and functions only. The private readings were read and are not quoted.

    Path: 「真挡得住的权限」 (permissions that actually hold) | access-security.anonymous-deny-surfaces (state ②) | P②
    Type: Bug. It breaks a declared contract: ADR-0056 D2 (anonymous default-deny, and "Mirror the change in the analytics read-scope path") and ADR-0090 D9 (a guest holds the guest position and nothing else).
    Triage: lands in packages/runtime (domain:cli); rationale: the door's missing authentication is what admits the unauthenticated member of the class on a stock boot, and that half can be dispatched now without touching a file an in-flight PR holds.

    P0, read from source at origin/main 8f784959cf

    The door.

    • packages/runtime/src/dispatcher-plugin.ts, the analytics block in start(): the cube-read, SQL and meta faces are plain server mounts that hand the request to the dispatcher. No authentication gate runs at the mount.
    • The dispatcher turns an unauthenticated request into the guest envelope (packages/runtime/src/security/resolve-execution-context.ts, via assembleExecutionContextOrGuest). That entry's own docblock says to adopt it only on a surface that serves anonymous principals, because it turns an authentication failure into an authorization evaluation.
    • packages/runtime/src/domains/analytics.ts, handleAnalyticsRequest: 0 calls of shouldDenyAnonymous. Control: ai, meta, security, actions, automation and packages in the same directory each call it (2 or 3 hits each).
    • The sibling doors carry it. In packages/rest/src/rest-server.ts, the record doors and the analytics dataset door each open with enforceAuth, so they answer the unauthenticated caller 401 UNAUTHENTICATED.
    • packages/qa/dogfood/test/authz-ledger-population.baseline.ts still lists the analytics dispatcher domain as an unclassified key. No conformance row has ever reviewed this door's posture.

    Behind it, in packages/plugins/plugin-security/src/security-plugin.ts. The analytics service reads both answers through packages/services/service-analytics/src/plugin.ts.

    • Object admission:
      • the engine middleware's step 2 CRUD gate runs only under permissionSets.length > 0;
      • canReadObject arm 2 returns true for an empty set;
      • canWriteObject (the organization wall alone) and canExport (true) carry a zero-set arm too.
    • Row scope:
      • for an empty set, computeLayeredRlsFilter collects no policy (collectRLSPolicies), so Layer 1 compiles to no filter;
      • getReadFilter therefore returns the sharing predicate alone, and that constrains only objects whose sharing model is private;
      • step 2.6's depth stash and checkAuthoredRowWrite ('abstain') also stand down for an empty set.
    • getReadFilter's principal-less branch assumes the analytics endpoint already refuses a caller without a token. This door does not.

    Why P0 (NORTH-STAR 〈优先级〉 rules 1 and 2):

    • Rule 1: it is security, at a public door, on the stock configuration, for the least-trusted caller. The caller gets counts over any object, platform identity objects included, and the card's measured reading agrees with this source read.
    • Rule 2: access-security.anonymous-deny-surfaces is a P0 item.
      • Its title asserts that every mounted API family answers 401 before any resource resolution.
      • The analytics family is mounted on that boot, and the item's steps never drive it.
      • That is state ② (the steps cannot reach the path), so the item's P0 is inherited, ⛔ not lowered.

    Direction: one shared rule at each layer, ⛔ no door-specific copy

    1. This card (domain:cli): the door requires the authentication its siblings require.

    • handleAnalyticsRequest opens with the shared decision, shouldDenyAnonymous (packages/core/src/security/anonymous-deny.ts). It is the first statement, ahead of the service-availability check and the body validation, in the hoisted form domains/security.ts and domains/ai.ts use.
    • Every analytics face (cube read, SQL, meta) answers the dispatcher-wrapper 401 UNAUTHENTICATED.
    • ⛔ No second gate at the mount, and ⛔ no zero-set admission rule copied into service-analytics.
    • Pins:
      • a runtime unit pin per face: anonymous gets 401 UNAUTHENTICATED, the service is never consulted, and a malformed body still gets 401;
      • the analytics family added to showcase-anonymous-deny-surfaces.dogfood.test.ts in the dispatcher-wrapper family, with a signed-in member as the 200 control;
      • an enforced row carrying a gate pin in authz-conformance.matrix.ts, and the analytics dispatcher-domain key leaving the shrink-only baseline;
      • the checklist item gains the analytics variant and its step. That file is docs/qa/**; declare it in the claim's file surface.
    • No pin title states a request.

    2. Split (domain:services): "no set resolved" is no grant, in plugin-security.

    Prior rulings read: anonymous,guest,unauthenticated,analytics → 23 hits; ADR-0056 D2, ADR-0090 D9, ADR-0106 D7; thread: none; repo: objectstack-ai/objectstack
    Size suggestion: S, one hoisted call plus the four pins above.


    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p0Critical: blocker, must ship before MVP
    and removed on Oct 1, 2026
  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 of the domain:cli seat's session session_01VvcEokUG1tvVxkceYfR5XB: priority:p0 security, emergency triage's first grade 5924543711 (direction 1, the door half). A P0 is taken at once, above batch
    Session: session_01VvcEokUG1tvVxkceYfR5XB
    Account: huangyiirene
    Branch: claude/issue-21061-analytics-anonymous-deny
    Worktree: objectstack-issue-21061
    Domain: domain:cli
    Seat: domain:cli#1
    File surface:

    • packages/runtime/src/domains/analytics.ts: handleAnalyticsRequest (:99 on 4f83db5a73) opens with the shared decision shouldDenyAnonymous (packages/core/src/security/anonymous-deny.ts).
      • It is the first statement, ahead of the service-availability check and the body validation, in the hoisted form domains/security.ts and domains/ai.ts use.
      • Every analytics face (cube read, SQL, meta) answers the dispatcher-wrapper 401 UNAUTHENTICATED.
      • ⛔ No second gate at the mount (dispatcher-plugin.ts is read only). ⛔ No zero-set admission rule copied into service-analytics.
    • Pins:
      • a runtime unit pin per face under packages/runtime/src/: anonymous gets 401 UNAUTHENTICATED, the service is never consulted, and a malformed body still gets 401;
      • the analytics family added to packages/qa/dogfood/test/showcase-anonymous-deny-surfaces.dogfood.test.ts, in the dispatcher-wrapper family, with a signed-in member as the 200 control;
      • an enforced row carrying a gate pin in packages/qa/dogfood/test/authz-conformance.matrix.ts, and the analytics dispatcher-domain key leaving packages/qa/dogfood/test/authz-ledger-population.baseline.ts (shrink-only);
      • the checklist item access-security.anonymous-deny-surfaces in docs/qa/platform-checklist/areas/access-security.json gains the analytics variant and its step. This is a declared cross-lane surface (docs/qa/**), per the grade.
    • One .changeset/21061-*.md for @objectstack/runtime.
    • ⛔ Not here: the plugin-security zero-set half (direction 2, split to its own domain:services card; security-plugin.ts is held by PR fix(plugin-security)!: a caller who resolves no permission set is served a masked field masked and may not query on it #21051). Also not here: packages/spec/** and the principal-less context.
    • ⚠️ Disclosure discipline: public text names doors, caller classes, codes and statuses only. ⛔ No request recipe in a PR body, changeset or test title. The private readings are not quoted.
      (stop on a breach outside these; explain in the report)
      Container & model: S, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable). P0 security; the grade sizes it S.
      Clause-②: no (narrowing)
      Clause-② reading: the analytics faces newly refuse an unauthenticated request they answered. That is an accept-set narrowing of a door against ADR-0056 D2, so the changeset carries the BREAKING banner under the launch-window convention. The dev re-reads this against the real diff.
      Thread-read: 5924543711
      Serial constraints cleared: No open PR touches packages/runtime/src/domains/analytics.ts, dispatcher-plugin.ts, the two dogfood files, the matrix, the baseline or docs/qa/platform-checklist/areas/access-security.json (the file lists of all open PRs were read in this act, main 4f83db5a73). PR #21051 holds plugin-security's security-plugin.ts, which is outside this surface. In flight on this seat: no dev. PRs #21043 and #21049 are in the merge queue, and neither touches these paths.

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    The plugin-security half split off by this card's triage (5924543711) is filed as #21079 · domain:services seat (#6021) · session_01XY5uCwTjZj7884yYtyur4H · 2026-10-01T04:59Z · ⚠️ Classes and positions only.

    #21079 is Blocked-by: #20995 (PR #21051, in the merge queue). It names the known consequences to measure, and the domain:spec docblocks the change would touch. This card's door half is unchanged by it.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21061,
    "status": "done",
    "branch": "claude/issue-21061-analytics-anonymous-deny",
    "pr": "#21098",
    "session": "session_01VvcEokUG1tvVxkceYfR5XB",
    "premise_still_valid": true,
    "summary": "handleAnalyticsRequest now opens with shouldDenyAnonymous as its first statement, ahead of the service-availability probe and the body validation, in the hoisted form domains/security.ts and domains/ai.ts use. The three analytics faces (cube read, SQL echo, meta) answer a caller without a session the dispatcher-wrapper 401 UNAUTHENTICATED; a signed-in member, an API-key caller and a system context are unchanged; no gate was added at the mount and nothing was copied into service-analytics. All four ruled pins landed: a runtime unit pin per face (analytics-anonymous-deny.test.ts); the analytics family in showcase-anonymous-deny-surfaces with a signed-in member as the 200 control; an enforced anonymous-deny-analytics matrix row carrying a GATE_PIN, with the /analytics dispatcher-domain key leaving the shrink-only baseline (MAX 33 to 32); and checklist item access-security.anonymous-deny-surfaces at revision 2 with the analytics variant, step and clause. Premise verified on origin/main 5dbeb7d before editing: 0 shouldDenyAnonymous calls in analytics.ts, and a stock boot answered an unauthenticated caller 200 on all three faces (table in repro). The claim's file surface needs one amendment by the seat: two files beyond it were required by gates the ruled change trips (see deviations).",
    "tests": "HEAD 8364f40. (1) runtime unit pin: pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/domains/analytics-anonymous-deny.test.ts -> 29 passed, 0 skipped. (2) runtime full suite: pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 at 3905d4f -> Test Files 301 passed, Tests 5029 passed, 5 skipped. Later commits touch no file under packages/runtime (git diff --stat 3905d4f 8364f40 lists only system-context.mdx and authz-conformance.matrix.ts). Before the fixture update the same suite had 117 failures in 15 files, all of them analytics route fixtures that drove the route with no identity. (3) pnpm --filter @objectstack/runtime typecheck (tsc --noEmit + check:test-typecheck, tsconfig.test.json reaches src/**/*.test.ts) -> exit 0. (4) dogfood, run against the rebuilt runtime dist: vitest run test/showcase-anonymous-deny-surfaces.dogfood.test.ts test/showcase-anonymous-deny.dogfood.test.ts test/authz-conformance.test.ts -> 3 files, 114 passed. pnpm --filter @objectstack/dogfood typecheck -> exit 0. (5) ABLATION, run after the fix was committed. The mutation replaced the gate condition with a never-true comparison against a planted literal, through scripts/ablation-replace.mjs (anchor x1 to x0, blob 3bb9a967 to 5e8fdfd6). Restore was proven both times: blob equals HEAD and git diff HEAD is empty. A plant marker was used because the deleted spelling is not unique in runtime dist. Source legs: the unit pin went 18 failed / 11 passed (every anonymous case red, every signed-in control green); authz-conformance went 6 failed / 49 passed, naming STALE covers analytics:domains/analytics.ts:anonymous-gate and DEAD PROBE packages/runtime/src/domains/analytics.ts. Dist leg: pristine preflight --absent exit 0; mutate, rebuild, preflight present exit 0 (marker in dist/index.js and dist/index.cjs); showcase-anonymous-deny-surfaces went 9 failed / 46 passed (5 anonymous analytics cases, 3 envelope classifications, 1 shared code-and-message case red; all 5 analytics member controls green); restore, rebuild, preflight --absent exit 0 (marker absent from 6 built files, tree clean). Direction: red as predicted in every leg. (6) Lint, a proven narrowing: eslint --no-inline-config --format json over all 24 changed paths at 8364f40. Population from eslint's own config: 3 non-TS paths ignored by configuration, 21 linted. Count from the json output: 21 files, 0 errors, 0 warnings. Invariance: eslint.config.mjs has no parserOptions.project and no typed rules, and the diff touches no lint config or rule input. (7) pnpm check:platform-checklist: only the pre-existing identity-auth.json problem (see gates).",
    "mcp_calls": "0",
    "api_writes": "3, all through the fleet-write relay as POST /repos/objectstack-ai/objectstack/dispatches: (a) pr_create executing POST /repos/objectstack-ai/objectstack/pulls, draft #21098, body read back byte-identical (9282 bytes); (b) label-write --assign huangyiirene executing POST /repos//issues/21098/assignees, read back matched; (c) this os-dev-report comment executing POST /repos//issues/21061/comments. Plus git push of the branch (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (no PR or person is known to be touching these files) · noted, not filed — the anonymous-deny.ts docblock in @objectstack/core names the five dispatcher domains ai/meta/security/actions/automation, and the endpoint-policy.ts table names /meta, /ai, /security; /packages and now /analytics also hold the floor (documentation drift, PR Acceptance notes)",
    "carrier: none · noted, not filed — check:platform-checklist is red on origin/main b3d7a70: areas/identity-auth.json anchors packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor, which is not declared there (manual-cadence ledger anchor drift; control run on main exits 1 with the same single problem)",
    "carrier: none · noted, not filed — the /analytics rows of packages/runtime/src/route-ledger.ts carry no authz: declaration naming anonymous-deny-analytics (optional, phased field; the classified /actions and /packages rows carry none either)"
    ],
    "gates": "dispatch-gates --repo objectstack-ai/objectstack --commands derived 93 families at 8364f40. All 93 were re-run after the final commit, with exit codes written to disk before reading: 92 exit 0, 1 exit 1. The red one is pnpm check:platform-checklist and predates this branch; control: the same command on origin/main b3d7a70 exits 1 with the identical identity-auth.json problem, and this branch touches neither that file nor the file it anchors. --ran reconciles 93 of 93 (0 NOT-MEASURED). Two gates were red on an earlier pass and are fixed in 8364f40: check-system-context-census (the new isSystem read site needed its anchor) and check:doc-authoring (a tracker id in the new row's summary string). Caveat: at reconcile time origin/main was at least 4 commits ahead (ci.yml, doc-line-anchors.mjs, regen-artifacts.mjs moved); that was not chased, and CI on the merge ref is the authority. CI on head 8364f40 at report time: 32 check runs, 11 success, 2 skipped, 19 in_progress (Lint & Repo Gates, Build Core and Temporal Conformance in_progress; Governed Surface Queue Guard success).",
    "deviations": [
    "File surface beyond the claim: (1) packages/qa/dogfood/test/authz-conformance.test.ts gets +1 GATE_PIN probe and +1 line in the exact-equality classified-key list. The ruled 'enforced row carrying a gate pin' can only be minted in that file's PROBES table, and its classified-key list is an exact equality that moves when the key leaves the baseline. (2) content/docs/permissions/system-context.mdx gets one anchor on row 51, and check-system-context-census --fix regenerated the counts 108 to 109. The floor reads ExecutionContext.isSystem, and that gate is red without the anchor. Both are declared in the PR body; the claim's file surface needs the matching amendment by the seat.",
    "Fifteen existing runtime suites under packages/runtime/src drove the analytics route with no identity. They now carry a signed-in caller: an auth slot answering a session, or the dispatcher's timedResolveExecutionContext stubbed the way the /packages fixtures already do. Only identity changed; no expectation changed.",
    "The branch merged origin/main once (3905d4f) so the gate derivation read a current tree, then refreshed install and build. No force-push.",
    "shouldDenyAnonymous is passed method as well as userId/isSystem, as actions/automation/packages pass it; the security.ts/ai.ts precedent passes only userId/isSystem. Only OPTIONS differs: it passes the floor and falls through to the existing handled:false. The analytics mounts register no OPTIONS route.",
    "The ablation planted a never-true comparison literal instead of deleting the call line. The deleted spelling also appears in automation.ts in runtime dist, so ablation-dist-preflight could not have used it as a unique marker.",
    "A shell-scoping slip wrote one pid file to the container root (/build-base.pid). It was moved into the scratchpad within about a minute; no other file landed outside the worktree and scratchpad.",
    "Lint was not run as full pnpm lint. A proven narrowing was used instead, with all three pieces of evidence in tests.",
    "The private boot runs the invite_only audience posture, so the signed-in member for the private probe was created through the admin create-user door rather than by self sign-up."
    ],
    "files_changed": [
    ".changeset/21061-analytics-anonymous-deny.md",
    "content/docs/permissions/system-context.mdx",
    "docs/qa/platform-checklist/areas/access-security.json",
    "packages/qa/dogfood/test/authz-conformance.matrix.ts",
    "packages/qa/dogfood/test/authz-conformance.test.ts",
    "packages/qa/dogfood/test/authz-ledger-population.baseline.ts",
    "packages/qa/dogfood/test/showcase-anonymous-deny-surfaces.dogfood.test.ts",
    "packages/runtime/src/analytics-authored-cube-format-granularity.test.ts",
    "packages/runtime/src/analytics-daterange-refusal-envelope.test.ts",
    "packages/runtime/src/analytics-json-dimension-door.test.ts",
    "packages/runtime/src/analytics-query-read-scope-withhold.test.ts",
    "packages/runtime/src/cross-field-refusal-operand-withhold.test.ts",
    "packages/runtime/src/declared-capability-absence-warn-once.test.ts",
    "packages/runtime/src/dispatcher-5xx-always-logged.test.ts",
    "packages/runtime/src/dispatcher-5xx-demoted-code-withhold.test.ts",
    "packages/runtime/src/dispatcher-plugin.declared-5xx-prose-withhold.test.ts",
    "packages/runtime/src/dispatcher-plugin.declared-user-message.test.ts",
    "packages/runtime/src/dispatcher-plugin.error-envelope.test.ts",
    "packages/runtime/src/dispatcher-validation-error.real.test.ts",
    "packages/runtime/src/dispatcher-validation-error.test.ts",
    "packages/runtime/src/domain-handler-registry.test.ts",
    "packages/runtime/src/domains/analytics-anonymous-deny.test.ts",
    "packages/runtime/src/domains/analytics.ts",
    "packages/runtime/src/http-dispatcher.test.ts"
    ],
    "repro": {
    "boot": "stock showcase, pnpm dev -- --fresh; before = BASE 5dbeb7d, after = merged branch 3905d4f build (the fix is identical at 8364f40)",
    "before": {
    "cube_read": {
    "unauthenticated": "200",
    "signed_in_member": "200"
    },
    "sql_echo": {
    "unauthenticated": "200",
    "signed_in_member": "200"
    },
    "meta": {
    "unauthenticated": "200",
    "signed_in_member": "200"
    },
    "cube_read_malformed_body": {
    "unauthenticated": "400 VALIDATION_FAILED",
    "signed_in_member": "400 VALIDATION_FAILED"
    },
    "sql_echo_malformed_body": {
    "unauthenticated": "400 VALIDATION_FAILED",
    "signed_in_member": "400 VALIDATION_FAILED"
    },
    "control_record_door": {
    "unauthenticated": "401",
    "signed_in_member": "200"
    }
    },
    "after": {
    "cube_read": {
    "unauthenticated": "401 UNAUTHENTICATED",
    "signed_in_member": "200"
    },
    "sql_echo": {
    "unauthenticated": "401 UNAUTHENTICATED",
    "signed_in_member": "200"
    },
    "meta": {
    "unauthenticated": "401 UNAUTHENTICATED",
    "signed_in_member": "200"
    },
    "cube_read_malformed_body": {
    "unauthenticated": "401 UNAUTHENTICATED",
    "signed_in_member": "400 VALIDATION_FAILED"
    },
    "sql_echo_malformed_body": {
    "unauthenticated": "401 UNAUTHENTICATED",
    "signed_in_member": "400 VALIDATION_FAILED"
    },
    "control_record_door": {
    "unauthenticated": "401",
    "signed_in_member": "200"
    }
    }
    }
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment: the file surface of 5924762402 gains three gate-compelled items (os-dev-report deviations, PR #21098)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T06:35Z · ⚠️ doors, caller classes, codes and statuses only

    Added to the declared surface. Each item is compelled by a gate the ruled change trips; none widens the direction:

    1. packages/qa/dogfood/test/authz-conformance.test.ts:
      • one GATE_PIN probe, because the ruled "enforced row carrying a gate pin" can only be minted in that file's PROBES table;
      • one line in its exact-equality classified-key list, which moves when the analytics key leaves the baseline.
    2. content/docs/permissions/system-context.mdx: one anchor row, with the census counts regenerated by check-system-context-census --fix (108 → 109).
      • The floor reads the execution context's system flag, and that gate is red without the anchor.
      • This is a declared cross-lane surface (content/docs/**). It is a gate-maintained row, not prose.
    3. Fifteen existing runtime suites under packages/runtime/src/ that drove the analytics route with no identity now carry a signed-in caller, the way the /packages fixtures already do. Only the identity changed; no expectation moved.

    The review of record judges all three on the PR.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment 2: two census files join the surface, for the Dogfood Regression Gate (3/3) fix (PR #21098, new head fa039bb8c5)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T06:52Z · ⚠️ doors, caller classes, codes and statuses only · follows 5926076065 and the PR note 5926114686

    1. packages/qa/dogfood/test/authz-probe-blind-spot.census.ts, updated the way its header prescribes for a new probe:
      • the PROBES table shape 18 / 13 / 15 → 19 / 14 / 17, and the matrix header claim 18 → 19;
      • one PROBE_FILE_CENSUS row for the analytics domain file (a gate pin, blind spot 0), with its same-file positive controls;
      • the file added to the domain gate-pin list.
        The blind-spot totals are unchanged.
    2. packages/qa/dogfood/test/authz-probe-blind-spot.test.ts: the "every classified key is accounted for" case asserts the table's key count, now 17, with its comment's figures brought current. No assertion is weakened or skipped.

    The fix is one fast-forward commit, with no force-push. The four authz and anonymous-deny dogfood files pass (149 tests), and the dogfood typecheck is green. The review of record is rendered on the new head.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #21098 at fa039bb8 (every analytics face the dispatcher routes refuses a caller without a session 401 UNAUTHENTICATED, the shared floor first). P0 door half

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T07:13Z · ⚠️ doors, caller classes, codes and statuses only

    • Contract review of record: 5926559083 on the PR, CONTRACT_REVIEW_TIER, head fa039bb8, PASS.
      • The gate: shouldDenyAnonymous is the first executable statement of handleAnalyticsRequest, ahead of the availability probe and the body validator. It uses the dispatcher-wrapper envelope and the shared constants.
      • All three faces (cube read, SQL echo, meta) converge on that handler, and the legacy HttpDispatcher.handleAnalytics delegates to it. An unknown sub-path is refused too.
      • The only other analytics door, the REST dataset query, already opens with enforceAuth. No reach is left open at the analytics doors for the unauthenticated member of the class.
      • Unchanged: a signed-in member, an API-key caller (by the shared decision; not driven by a pin) and a system context.
      • There is no gate at the mount and nothing in service-analytics. ADR-0056 D2 is met at the door, and ADR-0090 D9 holds.
      • Pins:
        • the runtime unit pin, per face × two anonymous shapes;
        • the dogfood family, with member 200 / 400 controls;
        • the enforced matrix row with its GATE_PIN, the baseline at 33 → 32;
        • the checklist item at revision 2, with history.
          Each reds without the gate.
      • The four gate-compelled additions (claim amendments 5926076065 and 5926289415) are each compelled. The fifteen runtime suites change identity only: every expect line is byte-identical. The census update weakens no assertion.
      • Semver: @objectstack/runtime minor with the BREAKING banner, Clause-②: no (narrowing), ADR-0087 not-required (no-migration-prescription). Right.
    • Seat actions on adoption:
      • The PR body gained an update section for head fa039bb8c5: the census fix, the two files and its verification. Read back identical. This was the record's housekeeping flag.
      • At the head, shouldDenyAnonymous( appears once in domains/analytics.ts.
      • A local git merge-tree against the current origin/main is clean.
    • Checklist:
      • Ready to queue, base main, first line Fixes #21061, Clause-②: no (narrowing).
      • 26 files, +657 / −92. NOT governed.
      • 35 check-run names: 33 success, 2 skipped (path-conditional), 0 red. All seven required contexts are green, including Dogfood Regression Gate (3/3), which was red on 8364f40c4a for this PR's own census and is fixed on this head.
    • Fixes closes the card correctly: direction 2, the zero-set caller behind the door, is security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079 (domain:services) by the triage split.
    • Notes, not findings:
      1. The anonymous-deny.ts docblock list and the endpoint-policy.ts table header are drift in @objectstack/core and @objectstack/runtime. Carrier: none.
      2. The check:platform-checklist red the report named is moot on current main (re-pointed by docs(qa): re-point the two-factor-disable mfa_required anchor at a declaration the resolver reads #21027).
      3. The route-ledger /analytics rows may now carry authz: 'anonymous-deny-analytics' under the docblock's own fill rule. It is a one-line follow-up for the next touch.
    • Next: land through the queue. At the merge, the Fixes closes this card, and the seat removes pm:dispatched.

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21098 → 2bddb19ccb (every analytics face the dispatcher routes refuses a caller without a session 401 UNAUTHENTICATED). The card is closed by the Fixes, and the seat removes pm:dispatched

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T07:42Z · ⚠️ doors, caller classes, codes and statuses only


    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:clipriority:p0Critical: blocker, must ship before MVPsecurity

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions