---
name: namespace-isolation-preflight
description: Review ownership, identifier scope, and isolation across users, organizations, agents, storage, caches, and asynchronous work. Use when a design or change introduces shared state, resource identifiers, cross-scope access, or data delivery; not for routine variable naming.
---

# Namespace & Isolation Preflight

Does everything belong to the right person, place, and scope, including after
the request ends?

Review the relevant code and contracts. A namespace is an identity boundary,
not automatically a permission boundary. A unique ID, directory prefix, or
hidden interface control does not prove isolation.

## Establish the intended boundaries

Identify the actors, resources, and supported sharing behavior. Distinguish
an organization, account, room, agent, installation, and machine when the
system actually gives them different authority. Do not invent a tenant layer
for a product that does not have one.

For each affected resource, record its canonical identity, owner, authorized
readers and writers, and allowed destinations. Separate the actor making a
request from the owner of the data and the audience receiving the result.
An agent's owner need not be the only person allowed to interact with it.
Preserve intentional collaboration; question accidental access or disclosure.

Use repository code and tests to distinguish enforced boundaries from labels
and intentions. Review-only requests authorize investigation, not migrations,
permission changes, or live access experiments.

## Follow identity from entry to effect

Trace a representative operation through its real producer and consumers:
request or tool call, authorization, lookup, mutation, queued work, and delivery.
Then examine alternate entry points that could bypass that path.

Check the mechanisms touched by the change:

- **Keys and queries.** Is an identifier globally unique or only unique within
  a parent? Do lookups, joins, mutations, uniqueness constraints, and bulk
  operations use the same scope? Resolve client-supplied IDs under server-verified
  authority. Never treat an unguessable ID as permission.
- **Names and paths.** Are display names being mistaken for identity? Check
  rename, case folding, Unicode normalization, prefix matching, traversal,
  symlinks, and extension or plugin name collisions where applicable. Follow
  the storage platform's actual semantics rather than assuming normalization.
- **Derived state.** Do cache keys, search filters, indexes, previews, and
  deduplication keys preserve the relevant scope? A filtered source query is
  not enough if a shared cache serves its result to someone else.
- **Subscriptions and delivery.** Check event channels, WebSockets, notifications,
  exports, generated files, and logs. Permission to read a source does not
  automatically authorize publishing it to a different audience.
- **Background work.** Does a queued or delegated task retain trustworthy actor,
  source scope, purpose, and destination? Retries and resumed tasks must not
  inherit whichever room or account happens to be active later.

Name the authoritative enforcement point. Reuse it where possible instead of
adding inconsistent filters to every caller. Preserve distinct read, write,
and disclosure decisions where the product requires them.

## Test changes over time

Trace the relevant transitions: rename, membership change, revoked delegation,
deletion, reconnect, retry, import, backup restore, and movement between servers.
Ask what happens to cached results, running tasks, signed links, and subscriptions.

State the intended revocation contract. Check authority again at the effect
or delivery boundary when a stale grant could violate that contract. A durable,
explicitly scoped grant may be valid; reconstructing authority from a mutable
display name or current UI selection is not.

Imports and restores must preserve or deliberately remap ownership. Do not
silently attach colliding IDs to unrelated local accounts. Identify how partial
migration, stale indexes, and old queued work are recovered or held safely.

## Prove separation and collaboration

Use synthetic principals and resources in an isolated test environment. Exercise
the real storage, cache, or delivery path where that mechanism determines the result.
Useful cases include two parents with the same local resource ID, a guessed
foreign ID, a warmed cache followed by another user, a revoked member with a
queued result, and an interrupted import retried after restart.

For each relevant boundary, prove both sides: the unauthorized operation fails
without leaking data, and the intended shared operation still works. Include
metadata and existence leaks when they matter to the product's threat model.
A correct denial should not be indistinguishable from a broken authorized path.

## Applying this to Nautilo

Use these checks when reviewing Nautilo, not as an architecture to impose on
other products. Verify the symbols in the checkout being reviewed; source
contracts do not establish which version a running server has adopted.

**Room authority is not agent ownership.** Start at `packages/trust/src/types.ts`,
the policy resolver, and `findReadableNamespacesForSubset` in
`packages/trust/src/queries.ts`. The namespace envelope separates readable,
mutable, and writable namespaces. Writable means attachment destination, not
permission to mutate every existing memory. Do not replace these sets with
one owner check. Scope-mode memory uses its bound scope and agent instead.
Tool arguments and model-generated text must not replace envelope identities.

**Check the audience rule in the correct direction.** For ordinary namespace-envelope reads,
the candidate room's human audience must contain the current room's human
audience. In a room with two people, one person's private namespace does not
become readable merely because their Genie is present. Conversely, do not
block intended reads from a room shared with both people. Follow
`packages/db/src/queries/namespace-access.ts` for public boundaries: matching
the stored human list alone is insufficient for an open room. Subthreads can
share the top-level room's namespace. Do not manufacture a new identity or
access boundary from a thread label.

**Attachments are not single ownership.** Inspect the memory/artifact namespace
junctions in `packages/db/src/schema/` and the access consumers. A memory can
have multiple attachments; scope-origin memory also retains its writable
origin. `resolveRequiredMemoryNamespaceIds` in
`packages/lattice-bridge/src/memory/required-namespace-set.ts` combines those
coordinates and rejects missing or stale origin authority. Do not simplify
that set to the active room or the first attachment.

**A cryptographic Domain is not a Namespace.** In the Lattice implementation,
separate stable Namespaces can share a Domain while retaining separate object
histories and keys. Follow the Namespace binding and keyring code under
`packages/lattice-crypto/src/namespace/` and the authority consumers in
`packages/lattice-bridge/`. Check namespace identity, Domain, Human/AI key
class, access revision, generation, and authenticated head together. A matching
Domain or cached key alone is not proof of current namespace authority.
Audience changes must follow the existing rebind protocol, not rewrite the
namespace identity or invent a parallel key store. Revocation cannot erase
plaintext or old keys already obtained by a former participant.

**Private work and return delivery are distinct.** Trace
`buildWideEnvelopeForSpeaker` and the private-task dispatch and completion
paths. An authorized excursion can read privately and bring selected work
back to its calling room. Check the persisted caller, return destination,
attachment target order, and result-text audience. A flag controlling artifact
attachment does not by itself make the completion message private. Preserve
authorized sharing without treating a wider read envelope as blanket disclosure.

Exercise the applicable cases, using existing tests as starting points:

- `packages/agent/tests/unit-isolated/no-namespace-traversal-via-tool-args.test.ts`:
  hostile tool content cannot substitute the bound speaker, agent, or scope.
- `packages/lattice-bridge/tests/scenarios/shared-domain-two-namespaces.test.ts`:
  both legitimate decryptions work and cross-namespace decryption fails.
- `packages/lattice-bridge/tests/unit/memory-required-namespace-set.test.ts`:
  retain the complete attachment/origin set; reject missing authority.
- For changed delivery code, test a private excursion returning to its original
  room after the user switches rooms, and separately test attachment and text
  disclosure. A passing crypto test does not cover that runtime path.

## Return the review

Lead with **Ready**, **Ready with named decisions**, or **Blocked** for the
reviewed change. Keep the report proportional to the findings:

- The affected boundary and the behavior that is actually enforced.
- Any blocker: trigger, affected path, consequence, and code evidence.
- The smallest coherent fix and a regression test for it.
- Unverified paths or a product-policy decision that code cannot settle.

Separate confirmed failures from plausible risks. Do not dump a checklist of
unrelated subsystems or call the entire application isolated because one path
passed. Security review can explore other attacks without repeating this map.
