P10 — Identity, settings and navigation host: research report
Run 2026-08-27-sprint-1-fable · lane S1-L4 · agent ACTIONIST-S1-L4-HOST Status: research only · UNEXECUTED · NOT_ADMITTED · admitted blocks 0
Question this part owns
Can one Actionist host give every capability a single identity, tenant model, settings hierarchy and navigation surface — absorbing donor chrome rather than stacking it — and what must be host-owned versus left donor-owned?
Exact denominators
| Evidence stream | Count | Notes |
|---|---|---|
| Commercial surfaces surveyed | 33 | 10 top10, 21 register, 2 rejected; 31 observed / 2 inferred |
| OSS projects surveyed | 35 | 10 top10 after backfill trim (31 + 4 backfilled) |
| Licence bodies read (OSS) | 8+ | including all four backfill repos and every suspected split |
| Targeted doc reads | 9 | 6 commercial reference docs + Luigi, Backstage, Frappe |
| Local sources read this run | 9 | see source-register.jsonl |
| Innovation candidates | 39 | 17 commercial + 22 OSS (16 + 6 backfill) |
Denominator honesty: the OSS packet stopped at 31 rows mid-run under a hard usage limit and named the four unfetched repos rather than inventing them; a targeted Opus backfill then fetched exactly those four, resolved the three highest-value open questions and trimmed top10 from 12 to 10. No row was padded to hit a count.
The headline finding
After 33 commercial surfaces, no observed surface renders a third party's settings inside host-owned chrome. Shopify, Forge and Superblocks leave the guest's settings screen wholly to the guest. Frontegg and Permit.io sell the admin UI as their own product. Zoho One and Microsoft 365 — the two largest suite-unification efforts observed — unify at the identity and admin tier while each product keeps its own UI, and explicitly do not suppress donor chrome.
This is absence of evidence after a deliberate sweep, not proof of impossibility. But it forces a correction to how A34 is tracked. The assumption ledger carries "the Actionist host can absorb donor identity/settings/navigation cleanly" as a single unknown. That conflates three tiers with very different precedent strength:
| Tier | Precedent | Cost class |
|---|---|---|
| Identity | Strong — 4 independent commercial forms plus a local protocol-proven spike | Bounded, patterned |
| Navigation | Partial — two proven halves, neither shipped standalone | Bounded per donor; host registry must be built |
| Settings | None found, commercial or OSS | Unknown; the lane's biggest risk |
The honest consequence: an Actionist promise of "one settings surface across all capabilities" is a research bet, not an engineering estimate. It should either be scoped to the commercial baseline for v1 (host-owned settings for host concerns; donor settings rendered by the donor behind a host-rendered nav entry) or made the deliberate differentiator with Loop-3 evidence behind it before it is sold to Cena.
What was established
1. Four named absorption patterns, each observed independently
A — iframe plus two-token exchange. The guest never brokers its own login; the host mints a short-lived signed assertion. Observed in four independent forms: Shopify App Bridge (session/ID token swapped via OAuth token exchange), Superblocks (org-level embed token held only by the host backend mints a short-lived per-user token with metadata sealed as claims), Salesforce Canvas (encrypted signed_request POSTed with user context — the same shape, twenty years earlier), monday.com (iframe talks to the parent frame, stores no keys, 5-minute server-signed token).
The consistent choice is two tokens, not one: a long-lived credential only the host backend holds, and a short-lived per-user token that crosses the boundary. Wix is the instructive counter-example — it delivers a signed instance as a query parameter and then warns in its own docs that the plain-text instanceId is attacker-manipulable.
This independently reproduces the local SISOCRM Ed25519 contract (60–120s lifetime, audience-bound, single-use jti, safe returnPath), which was protocol-proven by an executable spike. The local design was right and is commercially standard.
B — nav manifest registration, in three variants: deploy-time manifest (Forge — pages or sections but never both, host-assigned URLs), runtime component (Shopify's <s-app-nav>, relative paths only, one flat level, no nesting), and stored configuration document (ServiceNow — JSON on a UX Page Property, with the documented trap that a String-typed property makes the side menu silently vanish). Atlassian Connect is the only surface offering dynamic registration by REST, which matters if donors are absorbed at runtime rather than at deploy time.
C — scoped settings tree with an explicit combination rule. Every mature surface has a hierarchy and a stated conflict rule, and the rule is the design decision. Cerbos is the best published answer: two selectable modes — OVERRIDE_PARENT versus REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS, where a child ALLOW only lands if the parent would also allow and a child DENY always wins. Descope resolves cross-tenant conflict to the most restrictive policy. The idea worth stealing is making the combination rule an explicit per-node field with a safe default, not a global convention.
D — two-gate visibility. Frontegg states it plainly: a module enabled in the builder stays invisible without both the role permission and the feature entitlement, and the gates sit at different scopes so neither folds into the other. Salesforce adds what others omit — cascading emptiness: hide every component in a tab and the tab goes; hide every tab and the container goes. A host stripping entitlement-gated modules out of a donor needs exactly this or it leaves empty chrome behind.
E — console federation, the honest baseline: unify identity and admin, leave each product's UI alone. Mostly a warning.
2. One universal constraint
The host owns the URL space, the guest owns its internal router, and the guest must reconcile them. Observed without exception. Forge states it outright — the sidebar only changes the URL. Shopify's reported broken TitleBar links on direct URL visits are the same seam failing. This makes a route-reconciliation handshake at absorption time a requirement, not a refinement.
3. The navigation gap: two proven halves, no whole
The backfill resolved the survey's highest-value open question with direct doc reads.
Luigi's node tree is a genuine declarative contract — richer than assumed, with ~45 documented node parameters. Three are directly load-bearing: children is typed "array | function" and may return a Promise, so sub-trees resolve at runtime from a server; visibleForFeatureToggles gates a node on toggles with negation, a real precedent for nav-visibility-as-predicate; and userSettingsGroup ties a nav node to a named settings group — a rare existing case of navigation and settings sharing one declaration. What Luigi lacks is decisive: there is no contribution model. navigation.nodes is one tree authored in the host's single JavaScript config file. A micro frontend supplies a viewUrl the host has already written down.
Backstage supplies the other half. Auto-discovery is real — features install as soon as they are added to package.json — but it is welded to @backstage/cli and webpack, so it is a build-system coupling rather than a portable runtime mechanism. Plugins contribute nav by attaching to app/nav's items input, gated by an if predicate over feature flags and permissions. The limitation: a nav contribution outputs a React element, not a declarative node record. Backstage decentralizes who contributes; Luigi has the data structure. Neither has both.
Route refs are the highest-value transferable idea in the survey — opaque values representing route targets bound to concrete paths at runtime, so plugins link to each other without knowing any concrete path, including their own, with bindings declared once at startup and false explicitly disabling one.
4. The settings gap is the real greenfield
There is no OSS settings-schema library. Feature-flag platforms are the nearest precedent and are not the same thing: flat, environment-scoped key-value with targeting rules, evaluated per request. A settings registry needs hierarchy (system → tenant → workspace → user), schema and validation, defaults with override provenance, and typed rendering. Frappe's DocType is the closest conceptual relative and is whole-framework-bound, not extractable.
Combined with the commercial absence, this is the single clearest "build, don't buy" call in the lane — and the one with no precedent to de-risk it.
5. Authorization is oversupplied; building custom would be indefensible
Five credible Apache-2.0 engines across four genuinely different deployment shapes: OPA (CNCF graduated, embeddable as Go library, sidecar or WASM), SpiceDB (most mature Zanzibar, verified Apache-2.0 and not BSL), Cerbos (stateless PDP, YAML policies), OpenFGA, Casbin (in-process, RBAC-with-domains). Zero source-available traps in the category. The decision is deployment shape, not capability.
6. Licence traps continued
SuperTokens is a split licence — Apache-2.0 outside ee/, but ee/LICENSE.md requires a paid Enterprise licence for the correct seat count for any production use and forbids copying, distributing or sublicensing. The GitHub API reports NOASSERTION; the badge alone would have been actively misleading. This is the third instance of the Composio pattern in this project. NocoBase's Apache badge is misleading — LICENSE.txt is a custom Singapore-entity agreement prohibiting offering it as a public no-code/low-code/AI SaaS. Logto is MPL-2.0 (weak file-level copyleft, no network-service duty — the most absorbable multi-tenant IdP surveyed, but not permissive). Unleash is AGPL, not Apache. ZITADEL core and ToolJet are AGPL. Budibase is a three-way GPL/MPL/BSL split. Appsmith verified genuinely clean Apache-2.0. Separately: Auth.js/NextAuth's README now says it has joined Better Auth and recommends Better Auth for new projects, inverting the default TypeScript auth recommendation; Lucia is confirmed deprecated.
Invariants proposed
Eight, with falsifiers, in first-principles.md and decision-ledger.json. Load-bearing: the host is the sole identity authority with no reachable donor login route; two tokens never one and never in a query parameter; the absorption pattern is selected by reuse shape rather than platform preference; navigation is contributed data not shell source; a nav node's visibility predicate is its route guard; two-gate visibility with cascading emptiness; every resolved setting carries provenance; donor chrome suppression is declared in the absorption manifest and auditable, never done in CSS.
The contradiction this lane refuses to collapse
SISOCRM designed a full federation identity contract on 2026-07-30 and reversed it on 2026-07-31 in favour of absorption — "delete their login, one app, one session" — for donors whose code is owned, while Plane stayed host and infrastructure stayed service. Both decisions are correct in their context. The resolution is that identity lane is a function of reuse shape (decision-ledger.json carries the five-shape table), which means the host must maintain two identity lanes indefinitely. That cost is unmeasured.
Open decisions and unknowns
Five open decisions in the ledger; the consequential one is whether the settings tier is built as the differentiator or scoped down for v1. Unknowns carried forward: no commit-recency evidence anywhere in the OSS packet (the GitHub REST API was blocked in that agent's sandbox and returned null for every field, so every maintenance judgement there is inferred from secondary signals); Budibase's licence read from a competitor's blog rather than the LICENSE body; Permit.io pricing disputed across secondary sources; Piral's hosted feed service commercial terms unread; Salesforce Canvas's current status and Wix's deprecation timeline unresolved; ZITADEL's directory-level carve-outs not read at file level.
Experiments this feeds
Loop 3 (host absorption pilot on one mature donor) is the decisive vehicle — it must measure the settings tier specifically, because that is where the thesis has no precedent. Loop 0 tests whether a HostContract can express both identity lanes without becoming donor-specific.
Boundary
Nothing was cloned, executed, deployed or admitted. Public documentation only; no signup, no authenticated vendor access, no client-private data.