hivecast-sdk/packages/browser-host/src/boundary-identifiers.md

113 lines
5.6 KiB
Markdown
Raw Normal View History

# 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 `<matrix-dsl-host>`
- 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