# Boundary Identifiers — In-Package Reference If you're editing files in this package, you must know which identifier you're touching. Full spec: `WORKSTREAMS/platform-control-plane-freeze/FOUR-ROOTS-DISENTANGLE.md` (working title preserved for grep continuity). ## The Four Boundary Identifiers | Boundary name | What it identifies | Example | Where it crosses boundaries | |---|---|---|---| | **subjectScope** | NATS subject prefix for this Host's actors | `SPACE-HIVECAST` | `data-subject-scope` attr, `bootstrap.subjectScope` JSON field, `IBrowserBootstrapResponse.subjectScope` | | **authority** | Who owns this surface (user/principal) | `test-user-1` | `data-authority` attr, `bootstrap.authority` JSON field, `IBrowserBootstrapResponse.authority` | | **appName** | Which webapp this is | `director` | `data-app-name` attr, `bootstrap.appName` JSON field, `matrix.json:appName` | | **tabRuntimeId** | This browser tab's runtime identity | `test-user-1.DIRECTOR.SESSION-A1B2C3D4` | `data-tab-runtime-id` attr, computed client-side from `authority` + `appName` + session id | ## The discipline: clear at the boundary, flexible inside The names above are required at **interface boundaries** — anywhere code in this package talks to code outside it. That means: - HTTP bootstrap response fields - HTML attributes on `` - Exported TypeScript interfaces, types, and function signatures - matrix.json fields Inside private functions and private fields of this package, local variable names can use whatever feels natural for the surrounding code. The discipline is at the seams. ## Rules 1. **Boundary-crossing values use the canonical boundary name.** When you read a value from a config file, HTML attribute, JSON field, or exported interface, you read the canonical name (`subjectScope`, `authority`, `appName`, `tabRuntimeId`). You can rename it as a local variable inside your function if you want; just make sure the boundary read happened under the right name. 2. **One concept per boundary name.** Never write `setAttribute('root', ...)` in new code. The `root` attribute is ambiguous — it has meant all four identifiers at different times in git history. Pick the specific one. 3. **No fallbacks.** Per CLAUDE.md "no fallbacks or legacy crutches," if `appName` is missing from a bootstrap that needs it, **throw**. Do not silently degrade to using `subjectScope` in the `appName` slot — that's the wire-crossing bug this workstream exists to fix. 4. **Forbidden boundary names** (do not introduce in new code at boundaries): - `root` — meant all four at different times - `realm` — alias of `root`, same problem - `baseRoot` — got passed in as appName but is named after subjectScope - `rootAttr` — generic, ambiguous - `daemonRoot` / `daemonRealm` — pre-Host-Service legacy - `busRoot` / `authorityRoot` / `runtimeRoot` — these were in-flight rename drafts; superseded by `subjectScope` / `authority` / `tabRuntimeId` - `transportRoot` — classify each site; if the value has `.SESSION-XXXX`, the boundary name is `tabRuntimeId`; otherwise it's `subjectScope` - `spaceRoot` — use `authority` - `appRoot` — use `appName` or `tabRuntimeId` depending on which 5. **Allowed legacy aliases (compat only at boundaries, one release):** - `bootstrap.root` — still emitted for one release; readers must migrate - `bootstrap.platformRoot` — synonym of `subjectScope` during transition - `bootstrap.addressRoot` — synonym of `authority` during transition - `bootstrap.busRoot` / `bootstrap.authorityRoot` / `bootstrap.runtimeRoot` — in-flight rename names from earlier drafts; emit both old and new for one release so any consumer already coded against them keeps working ## HTML attribute writes are intent-explicit When you write attributes during browser bootstrap, write the specific canonical attribute name: ```ts this.setAttribute('data-subject-scope', subjectScope); // not 'root', not 'realm' this.setAttribute('data-authority', authority); this.setAttribute('data-app-name', appName); this.setAttribute('data-tab-runtime-id', tabRuntimeId); ``` The legacy `root` attribute is still written during the transition release (transport readers in older code path still consume it as `subjectScope` in hosted mode). New code reads `data-subject-scope` instead. ## Why this exists Through git history, "root" has meant all four of these identifiers at different times. `commit d73fc73ee` (May 14, 2026) introduced `busRoot` as a separate bootstrap field when Space-claimed users couldn't load the platform shell. That fix worked for transport, but the HTML attribute `root` is read by THREE consumers expecting THREE different meanings, so writing `busRoot` into it broke tab identity downstream (`tab_realm.ts:generateIdentityTabRoot`, whose `appName` parameter was being fed `busRoot` until this workstream landed). The lesson: **at boundaries, names that say what they mean prevent the wire-crossing class of bug.** Internal variable names can stay flexible; the boundary names cannot. ## Where each identifier comes from - **subjectScope** — server-side, derived from `status.transport.root` of whichever Host's gateway answered the HTTP request - **authority** — server-side, the signed-in principal's `primary_authority_root` (or, on Device hosts, the Device's linked authority from `host-auth.json`) - **appName** — server-side, derived from the route's app context (`/apps/director/` → `director`); declared in the package's `matrix.json` - **tabRuntimeId** — client-side, generated as `${authority}.${appName.toUpperCase()}.SESSION-${sessionId}`, persisted in sessionStorage