Guides

Collection egress policies

Give each collection a fail-closed transport boundary that follows its content through inference, publishing, exports, Capsules, and traces.

Local-first is not one setting. Most real setups are mixed: your open notes are fine on a LAN inference box, the client engagement is not, and the vendor contracts should never reach a remote endpoint under any circumstances. “Is GNO local?” is the wrong question. The right one is “can this content reach that destination?”

Egress policy answers it per collection, and enforces the answer at the point of transfer rather than at the point of configuration.

The three levels

PolicyContent may reach
local_onlyThis machine. Loopback destinations only. No LAN model server, no remote inference, no publishing.
lanThis machine plus private-network destinations, such as a GPU box in the same network.
remoteAny configured destination, including a remote inference endpoint or gno.sh publishing.

Policies loosen in the order local_only lanremote. Existing collections migrated to local_only: fail-closed, so an upgrade could never widen a boundary you had not thought about. Migration preserves local retrieval and index data in full.

Reading and checking a policy

gno collection policy get notes
gno collection policy check --action export --destination remote \
  --content-class retrieval_trace --collection notes \
  --authenticated --authorized --explain-egress

get shows the effective policy, where it came from, and its numeric revision. check is a dry run: it answers “would this specific transfer be allowed” without attempting it, which is what you want in CI, in a runbook, or before wiring up a remote model. --explain-egress shows the reasoning.

Relaxing a policy

gno collection policy set notes local_only
gno collection policy set notes remote --confirm-relaxation 7

Tightening is immediate. Loosening is never inferred: run get, look at the current value and its source, then pass that exact numeric revision to --confirm-relaxation. The revision is durable and single-use. Stale, replayed, cross-collection, and cross-target confirmations all fail closed.

That handshake exists so a relaxation cannot be scripted blind. You have to have read the current state, in this collection, at this revision, to widen it. An agent that automates policy set without a fresh get simply fails.

Tightening invalidates resident sessions and queued work. Jobs that were in flight under the older, looser policy must be retried and rechecked rather than completing under an assumption that no longer holds.

Policy follows the content

This is the part that separates a real boundary from a checkbox. A policy that applied only to the original file would be trivially escaped: build a Capsule from a restricted collection, then publish the Capsule.

Instead, derived artifacts inherit source policy. Snippets, embeddings, Context Capsules, retrieval traces, adapter records, and exports all carry the policy of what they came from. When evidence mixes several collections, the most restrictive participating policy governs the whole thing. One local_only passage in a Capsule makes that Capsule local_only.

Each Capsule evidence item exposes its own egress value and lineage, and the aggregate lineage must account for every contributing collection, so the boundary is inspectable rather than implicit.

Three independent gates

Authentication, write authorization, and egress are separate checks, and passing one never satisfies another:

A valid bearer token on a non-loopback daemon still cannot pull local_only content off the machine.

How destinations are classified

Classification is conservative by design and covers loopback, LAN, VPN, proxy, redirect, and remote destinations. Cases that would otherwise be laundering routes fail closed: DNS rebinding, redirects to a different class than the one you checked, mixed evidence, active streams, queued jobs, and stale relaxation confirmations.

Denial reasons are stable and content-free across CLI, REST, MCP, SDK, and Web. An error message never leaks the passage it refused to send, the path it came from, or a credential.

Audit receipts

gno egress-audit list
gno egress-audit show <audit-id>
gno egress-audit status
gno egress-audit delete <audit-id>
gno egress-audit purge

Receipts are local, bounded, and redacted: what action was attempted, against what destination class, with what outcome. Never the content. Audit inspection and deletion keep working even when an outbound action was denied, so a refusal is always diagnosable.

A practical setup

# Client work never leaves the laptop
gno collection policy set client-engagement local_only

# Personal notes may use the GPU box on the LAN
gno collection policy get notes # note the revision
gno collection policy set notes lan --confirm-relaxation 3

# Verify before wiring a remote endpoint
gno collection policy check --action remote_inference --destination remote \
  --content-class passage --collection notes --explain-egress

One caution worth repeating: egress policy governs transport, not authorization to use content. It stops bytes crossing a boundary. It does not encode who is allowed to read what, and it is not a substitute for keeping confidential material in the right collection in the first place.