# Boundary Identifiers — In-Package Reference

If you're editing files in this package, you must know which identifier
you're touching. This file is the in-package boundary contract for the SDK
browser host. Provider overlays may maintain their own deployment-specific
mapping docs, but boundary names exported by this package stay provider-neutral.

## 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 implicit substitution.** If `appName` is missing from a bootstrap that
   needs it, **throw**. Do not silently degrade to using `subjectScope` in the
   `appName` slot; that creates wire-crossing bugs.

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
