Version. 0.1 Date. 2026-07-17 Author. Claude.ai, on Operator direction. Status. First scoping note. Nothing here is built. Grounded in two inspections filed the same day; where those inspections say a thing does not exist, this note treats it as not existing rather than assuming it can be reached for. Grounding. The agentic identity investigation (2026-07-17) and the authenticated marketing access inspection (2026-07-17), both by Claude Code against live source. Change Request 2026-152 (the sales perspective spike), sections 1 and 8. Naming. "Operator tools menu" is a working name. See section 10.
What this scopes. A menu inside the Operator Layer that lists the tools the Operator uses — the sales perspective generator today, a FORAY generator in design, more after that — and makes them reachable only by people the Operator has authorised. The person signs in with Stele, as they already do. The menu then hands them a signed pass. Each tool checks the pass before it does any work.
What changed, and why this exists. The sales perspective generator was built at the edge, on the marketing site, and needed a gate. The gate reached for was Cloudflare Access — a different company's identity system, in front of a company that builds identity systems. The Operator's standing direction is that anything authenticating a person in a DUNIN7 system uses Stele. This note is how that direction is met.
What is new. Three pieces: the menu surface, an endpoint in the engine that mints a signed pass, and a check in each tool that verifies it. Everything else already exists.
What decision rests on this. Whether to write a Change Request against this design, and whether the pass travels in a link or is copied by hand. See section 10.
Out of scope. Agents. Per-tool authorisation. The public Example Generator, which is public by design and has no identity to prove.
Change Request 2026-152 built the sales perspective generator as Cloudflare Pages Functions on the marketing site. It works. It has no gate, and on 2026-07-17 a preview deployment was briefly reachable by anyone holding the address, with four endpoints spending Anthropic budget behind it. The preview was deleted.
The gate designed for it was Cloudflare Access — a perimeter in front of the paths, using Cloudflare's identity rather than Loomworks's.
The Operator's direction, 2026-07-17: anything that authenticates a person in a DUNIN7 system uses Stele. Otherwise the company never proves to itself, or to anyone else, that its own identity substrate works. The moment authentication was needed, the stack reached for someone else's. That is the finding, and it is the reason for this note.
The Operator also confirmed that more tools are coming — a FORAY generator is in design. So this is infrastructure built once, not a one-off gate.
Every claim in this section comes from a Claude Code inspection of live source on 2026-07-17. None of it is assumed.
Stele authenticates humans, and only humans. WebAuthn passkey as first factor, time-based one-time password as second. The engine then sets a session cookie named loomworks_session — HttpOnly, host-only, SameSite Lax, holding a stateless Fernet-encrypted token with a twenty-four hour lifetime. There is no server-side session store.
Stele issues nothing else. Its own contract: "Stele proves who a caller is; your app decides what they may reach." No downstream tokens, no delegation, no authorisation. Everything past resolving the principal is the host's business.
Stele's session cannot be verified at the edge. It is sealed with a symmetric key. Anything that can verify it can also mint it. A Pages Function could only check it by holding Stele's own sealing secret, which is not distributable. There is no public-key story for a Stele session. Any edge-verifiable artifact must be minted by the engine on top of Stele.
The engine mints no asymmetric signatures today. The cryptography package is installed, so Ed25519 is available, but nothing in the engine imports or uses it. The engine's only asymmetric operation is verifying WebAuthn assertions, where the private key lives on the person's authenticator.
A Cloudflare Pages Function can verify Ed25519 natively, through the Workers WebCrypto implementation, with no library. The marketing project imports no cryptography today and pins no compatibility date, because it commits no Wrangler configuration — bindings are set on the Cloudflare dashboard.
The marketing site and the engine are on different registrable domains. loomworks.doneinseven.com and dunin7.com. No cookie can cross between them; this is a browser rule, not a configuration gap. The session cookie is additionally host-only, so it does not even span subdomains of its own setter.
The engine has no redirect flow of any kind. No OAuth, no single sign-on callback, no magic link. Every authentication endpoint is a JSON POST that returns a body and sets a cookie. The browser is never sent a Location header.
The engine's production cross-origin allowlist is empty by default. No marketing origin is permitted today.
The engine's public reachability is not established in its own repository. No tunnel or deployment configuration is committed. Documents describe an intent to sit behind a Cloudflare tunnel at api.loomworks.dunin7.com. The Operator Layer's production hostname is stated nowhere.
Authorisation in the engine is coarse. require_operator_designation checks a designation from a fixed three-value set on a membership. require_founder compares the caller's identifier against a single stored platform-founder identifier, and fails closed if unset. There is no concept of "may person X use tool Y" for humans. The one per-capability model, verify_companion_authorization, authorises an agent, not a person.
The Operator Layer has no menu. A signed-in Operator lands on a list of engagements. There is no tool list, no launcher, no extensible navigation. A tools menu is a new surface.
Stele Agentic ID has no edge story. It is a separate product with its own registry, sharing no principals table and no session with human Stele. Its verifier returns a verdict — pass or fail with a reason — not a bearer credential. There is nothing an edge function could hold and present.
The pass does not travel over a network. It travels in a link, through the Operator's browser.
That single choice removes most of what section 2 says does not exist. The engine and the marketing site never speak to each other, so no redirect flow is needed, no cross-origin allowlist change is needed, and the engine needs no public reachability beyond what the Operator Layer already requires of it.
A new surface in the Operator Layer, reachable when signed in. It lists the tools. For each, it shows what the tool is and offers a way in.
The Operator Layer already authenticates against the engine and already carries the session cookie first-party. The menu adds no authentication of its own — it is behind the same Stele sign-in as everything else in the Operator Layer.
The menu is data-driven from the start. A new tool is a new entry, not a new gate. That is the whole reason this is infrastructure rather than a one-off.
A new endpoint in the engine. It requires an authenticated principal — the existing get_current_principal dependency, which resolves the session cookie and enforces that the second factor was completed. It then checks authorisation (section 4) and, on success, mints a short-lived pass.
The pass is a signed statement, not an encrypted one. Ed25519, using the cryptography package already installed. It carries the person's identifier, the tools it is good for, an issued-at time, and an expiry. Thirty minutes.
The engine holds the private key. Nothing else does. The public key is not secret and is distributed freely.
This is the only genuinely new capability in the design. Everything the mint depends on — resolving the session, checking a designation — exists and works today.
The menu asks the engine for a pass and builds the tool's address with the pass in the fragment — the part after the #. The Operator clicks. The browser sends the fragment to no server, and it appears in no server log.
The tool's page reads the fragment on load, immediately clears it from the address using history.replaceState, and holds the pass in memory for the session. Every call to the tool's endpoints presents it.
This is the design's weakest point, and it is named rather than hidden. See section 6.
Each tool endpoint verifies the pass before doing any work: signature valid against the public key, not expired, and naming this tool. Failure is a refusal, not a redirect — the tool does not know how to authenticate anyone and should not learn.
The public key reaches the Pages Function as an environment variable, the same mechanism ANTHROPIC_API_KEY uses today. Verification is crypto.subtle.verify with no library.
A tool that cannot verify does not serve. If the public key is absent, the endpoint refuses. This is the one place the design deliberately departs from the existing rate limiter's fail-open posture: a limiter failing open costs money, an authorisation check failing open costs the gate.
Version one asks require_founder. It exists, it is fail-closed, and the Operator is the only user. The mint refuses anyone else.
That is deliberately the smallest thing that works. The alternative — inventing a per-tool authorisation model — would be building GRANTHA's job, in the wrong place, against a substrate whose own boundaries are, per its Ratification Ledger, largely reconstructed with four ratified items. That would have to be unpicked.
The seam is clean because GRANTHA's own model makes it clean. GRANTHA's stated posture: "GRANTHA authorizes; it never enforces. Every enforcement point is a local governor that must ask and honor the answer."
The mint endpoint is a local governor. It asks, and it honours the answer by minting or refusing. The pass transports an authorisation decision; it does not make one. When GRANTHA lands, the mint changes what it asks. It does not change what it does, and nothing downstream of it changes at all.
That is why this design does not wait for GRANTHA and does not duplicate it.
Agents. Stele Agentic ID returns a verdict from a bounded chain walk against a signed registry statement, not a credential. There is nothing for an edge function to hold. If agents ever need to reach these tools, that is a different mechanism, and it would rest on acts_for — a nullable pointer to a human whose consent and binding model the agentic product's own version two exclusions mark as deliberately undecided. Named as out of scope with a reason, not deferred silently.
Per-tool authorisation. Version one is founder-or-nothing. Which person may use which tool is GRANTHA's question.
The public Example Generator. It is public by design — a stranger describes their work and sees what Loomworks would do. Requiring an identity to view a marketing illustration would ask someone to enrol a passkey before they know what is being sold. There is no identity there to prove because there is no identity. If it ever needs gating, that is a credit question, not an authentication one.
Replacing Cloudflare Access everywhere. The Harvest Surge mockups and the record site are gated by Access and this note does not touch them. Those are static documents behind a perimeter, not tools authenticating a person.
The pass is in the address bar, briefly. Fragments are not sent to servers and not logged, which is why this is the standard choice for exactly this problem. But the address is in browser history until cleared, and an address copied and shared is a working credential shared. The thirty-minute lifetime bounds it. Clearing the fragment on read bounds it further. Neither eliminates it.
The alternative is section 9's copy-and-paste, which has no address to leak and is worse to use.
The two clocks. A pass minted at one moment asserts an authorisation decision made at that moment. If the decision changes inside the pass's lifetime, the pass is stale and the tool does not know.
This is not a new problem and should not get a new answer. GRANTHA has it open under the same name — carried-grant freshness, the two-clocks problem, marked in motion. A thirty-minute lifetime is this note's interim answer. When GRANTHA settles the question, this should adopt that answer rather than keep its own.
Key custody is unestablished. The engine holds only symmetric secrets today, through environment configuration. Where an asymmetric private key lives, how it rotates, and what happens when it is compromised are not answered by anything in the repository. Rotation is not designed here.
Nothing is proven about production hostnames. Neither repository pins them. The Operator Layer's production host is stated nowhere. This design does not depend on same-site between the Operator Layer and the engine — the menu talks to the engine the way the rest of the Operator Layer already does — but the deployment shape is an operational fact the code does not settle.
Change Request 2026-152 section 1 says the real build goes in the Operator Layer if the thesis holds. It held. Moving the sales generator into the engine would remove the cross-domain problem entirely: no pass, no mint, no verify, no fragment.
Set aside, because the Operator confirmed more edge tools are coming. A FORAY generator is in design, and FORAY already runs a live worker at the edge. Porting each tool into the engine pays the port cost every time and forecloses the edge chain that works today. Building the pass once pays once.
This is a genuine trade, not a free win. Two deployments must stay in step. A tool at the edge cannot consult the engine cheaply. If the edge tools stop multiplying, the port becomes the better answer and this design becomes overhead.
The menu displays the pass. The Operator copies it and pastes it into the tool.
Better on every security axis. No address to leak, no history entry, no fragment machinery, no history.replaceState. The pass exists only where it is pasted.
Worse on every use axis. Two extra actions every time, on a tool opened several times a week, by the person who built it.
This note recommends the fragment and records this as the reversible alternative. If the fragment's history exposure proves uncomfortable in practice, this is the fallback and it requires no redesign — the mint and the verify are unchanged. Only the carry differs.
None of these can be answered from Claude.ai.
require_founder in a way a mint would disturb? The inspection found it gating three credit-grant routes.Recorded here because they surfaced during it and should not be lost.
~/loomworks/.dev.vars and ~/loomworks-marketing/.dev.vars. Both are ignored by version control, but they are real secrets on disk and worth rotating./welcome, and no such route exists.DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Operator tools menu and authenticated edge access — Scoping note — v0_1 — 2026-07-17