Skip to content

feat(plugin-security): under a walled posture, deliver the access upgrade report in-app into the organization OS_PLATFORM_OWNER_ORGANIZATION names, instead of the operator log line only #25413

Closed

Description

@objectstack-fleet

Filing gate ③: a direction the maintainer gave in the epic seat's chat on 2026-10-23, on #24446's walled delivery. The seat had put option C (an organization-less, operator-only notification) to the maintainer. The maintainer chose instead to configure the platform owner's organization, the way the platform owner's email is configured: 「既然可以配置平台管理员的邮箱,也可以配置平台管理员的组织id吧?」, naming it OS_PLATFORM_OWNER_ORGANIZATION.

  • Who acts: the shared queue.
  • Dedupe: none. No platform-organization setting exists on main; packages/types/src/env.ts has OS_PLATFORM_OWNER_EMAIL only.

Related: #24446 (closed), U1 PR #25311, the seat's option-A ruling 6353787407, #24447 (the non-access classes after v18, which meet the same walled-delivery question).

What is on main

  • U1's boot report (packages/plugins/plugin-security/src/access-upgrade-report.ts) raises access-class findings at kernel:bootstrapped:
    • under single, through messaging.emit() to the Default Organization's admin_full_access holders plus OS_PLATFORM_OWNER_EMAIL, stamped with the Default Organization;
    • under a wall (group / isolated), only as one error line addressed to the operator (option A).
  • Why the wall gets only a log line: sys_notification is organization-owned (ADR-0131: no NULL organization_id), a walled system write that names no organization is refused (D9), and the operator belongs to no organization by default.

Wanted

  1. A new setting, OS_PLATFORM_OWNER_ORGANIZATION, beside OS_PLATFORM_OWNER_EMAIL in packages/types/src/env.ts:
    • its value is the organization's id or slug, resolved at boot;
    • it is read only under a walled posture; single keeps the Default Organization.
  2. Delivery under a wall when it is set: U1's report emits the same security.access_upgrade notification, with the same dedupKey per finding set, stamped with that organization and addressed to the operator or operators (OS_PLATFORM_OWNER_EMAIL). The error log line stays beside it, as it does under single.
  3. Fail loud, never fall back silently:
    • set but naming no organization: one boot error naming the value and the fix, and the log-line delivery;
    • set, but the operator is not a member of that organization: a warn that the in-app copy will not be visible to them.
  4. A dedicated organization. The findings are deployment-level and can name other organizations' ids and sharing-rule names, so any administrator of the named organization can read them in Setup, Diagnostics, Notification Events. Document that the organization must hold operators only. Measure whether a boot warn can cheaply flag an administrator of it who is not a configured operator, and add it if so.
  5. Not set: unchanged. Option A's log line only.
  6. Shape it for reuse: make the walled delivery a small helper the upgrade-impact comparison after v18: report the non-access classes to the administrator, and retire the zero-consumer package-upgrade.zod.ts #24447 report can call, rather than a branch inside U1's report.

Acceptance

  • Under isolated and group, with the setting naming a dedicated organization the operator belongs to: a vendor rename produces one notification in that organization, visible in the operator's inbox, and none in any other organization. It deduplicates on the next boot.
  • An unknown value, and the setting left unset, behave as in Wanted 3 and 5. Each is pinned, with a CONTROL.
  • single is unchanged.
  • The changeset declares Clause-②: yes (a new setting) and Metadata-protocol: none, at patch. The env docs list the setting next to OS_PLATFORM_OWNER_EMAIL.
  • R1–R4 hold, with R4 measured under isolated and group.

Activity

  1. added
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    on Oct 23, 2026
  2. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card thread · domain:services seat 1 (seat post #6021, project run by the Sales account) · p3 · the whole card · 2026-10-23T14:59Z
    Session: session_01XHebWJmL3ZgbVpjM6JERNA
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-mfvys8 (the harness-pinned branch for this session; it names no issue, so the Duplicate Fix Guard's branch-name hint will not match — this claim is the identity record). This card's PR lands first on it; #24447 (claimed beside this one, same session) follows on the same branch after this PR merges, because its report calls the delivery helper this card adds.
    Worktree: objectstack-issue-24447
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (read on origin/main b2c07529; stop on a breach and explain it in the report):


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25413,
      "status": "done",
      "branch": "claude/project-thread-mfvys8",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25489",
      "session": "session_01XHebWJmL3ZgbVpjM6JERNA",
      "premise_still_valid": true,
      "summary": "Under group and isolated, OS_PLATFORM_OWNER_ORGANIZATION (an id or slug, read only under a wall; PLATFORM_OWNER_ORGANIZATION_ENV and resolvePlatformOwnerOrganization() in @objectstack/types beside the email pair) now raises the access upgrade report in-app: the same security.access_upgrade notification with the same dedupKey, stamped with that organization and addressed to the OS_PLATFORM_OWNER_EMAIL operators, with the operator log line kept beside it. A value naming no organization logs one error naming the value and the fix and raises nothing in-app (no fallback); a configured operator who is not a verified member, and an administrator of that organization who is not a configured operator, each get a warn. The single and walled delivery now lives in one helper, plugin-security's upgrade-report-delivery.ts, which #24447 calls next; reportAccessUpgrade hands its finding set to it, and single is byte-identical. security-plugin.ts, packages/core, packages/spec and service-messaging are not touched.",
      "tests": "plugin-security pnpm test: exit 0, 215 files, 4485 passed, 45 skipped. New upgrade-report-delivery.test.ts: 17 cases, each with a CONTROL (id, slug, trimmed value; unset and blank; unknown value; unreadable sys_organization; no messaging; refused emit; no operator; non-member, no-account and unverified operators; administrator by grade and by grant; unreadable sys_member; single with the setting set and unset). access-upgrade-report.wiring.test.ts on a real ObjectQL + SqlDriver + SecurityPlugin: isolated and group with the setting (one emit, organizationId = the owner organization, audience [operator], operator line beside it, same dedupKey on the next boot) and isolated with an unknown value; the existing cases pass unchanged. types env.test.ts: 58 passed. Ablations (committed source mutated with scripts/ablation-replace.mjs, restored, git diff HEAD empty): walled branch forced to the unset path = 16 red; membership warn off = 2 red; admin grade not read = 1 red; slug lookup off = 4 red. R1: showcase dev boot logs the access report's clean info line, 0 ERROR. R4: production os serve of the showcase on persistent SQLite under isolated and group (clean, CONTROL, rename with the setting naming a dedicated r4-platform-ops organization, rename again), plus isolated with an unknown value, unset, an admin member added, and the setting pointing at a tenant. On the rename boot there is one sys_notification in org_r4_platform (dedup security.access_upgrade:a9b33319418033cf427003ea5c64528e), one delivery and one sys_inbox_message for the operator, and none in any other organization; the next boot dedups. The table is in the PR body.",
      "gates": "Local: all 100 commands dispatch-gates --commands derived, exit 0, reconciled with --ran. Two needed a second run: check-plugin-teardown-shape --self-test exited 3 in a shallow clone and exits 0 after git fetch --unshallow; check:where-matcher flagged the new double, which now refuses a combinator, and exits 0. check-governed-merges --branch: not governed. PR check-runs on head 07d0ee18, all success: Build Core, Build Docs, Test Core 1/6 to 6/6 and the Test Core aggregate, TypeScript Type Check, Type Check · workspace, source gates, consumer gates, debt ledger, Lint & Repo Gates, Lint · ESLint, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · scoped (tooling self-tests, query-options erasure, slot lookup & corpus), Dogfood Regression Gate, Spec property liveness, Governed Surface Queue Guard, Check Changeset, Check PR Size, Check Documentation Links, Flag docs affected by code changes, the three claim and single-writer guards, Part-of PR must not also close its card, Auto Label; the opt-in and matrix jobs skipped by design.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "28 — reads only: issue_read 11, pull_request_read 7, list_pull_requests 4, list_issues 3, search_issues 3 (this session, both cards); no write tool",
      "api_writes": "0 direct REST writes. Every GitHub write went through the scripts/pm fleet-write relay: post-stamped.mjs for the claim 6367880115, label-write.mjs for the assignee and the pm:dispatched label, and fleet-write/dispatch.mjs pr_create for #25489 (opened as a draft)",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · 2026-10-23T17:40Z

    reading value
    PR #25489 MERGED through the queue at 2026-10-23T17:02:49Z (TIER_DEFAULT record PASS and the seat's ACCEPT are on the PR), head 07d0ee185b
    origin/main carries 915e940499

    State: Fixes #25413 closed the card. pm:dispatched is now off and the assignee is cleared. The delivery helper upgrade-report-delivery.ts is on main, and #24447's services half (PR #25512) calls it.


    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

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions