518 lines
29 KiB
Markdown
518 lines
29 KiB
Markdown
# Gloved session policy
|
|
|
|
`gloved` validates remote identifier-only plans against operator-owned local
|
|
policy. Remote requests cannot supply executable paths, host paths, environment
|
|
variables, or raw secrets.
|
|
|
|
## Capability activation
|
|
|
|
| Configuration | Advertised session methods |
|
|
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
| Base service | authenticated receipt paging and acknowledgement |
|
|
| `--session-policy` | `validate_plan` |
|
|
| plus `--session-store` | `create_session`, `session_status` |
|
|
| Linux plus `--materialization-root` | `start_session`, `attach`, `write_stdin`, `resize`, `signal`, `detach`, `stop_session`, `cleanup_session` |
|
|
| validated `remote_backend` | construction diagnostic only; no lifecycle methods |
|
|
| plus `--library-bundle-root` | digest-bound read-only bundle projection |
|
|
| `--path-exposure-policy` plus `--path-exposure-journal` | local exposure administration and redacted catalog |
|
|
|
|
Capabilities are derived from successfully constructed and operational
|
|
components, not pointer presence. macOS does not advertise managed-session
|
|
resource enforcement. The construction-only remote runtime reports
|
|
`lifecycle_operational() == false`, adapter schema `0`, no managed runtime IDs,
|
|
and no resource or verified-receipt capability. Its local lifecycle methods are
|
|
unavailable through the protocol even though validated SSH artifacts and a
|
|
runtime object exist.
|
|
|
|
`refinement_evaluation_protocol_schema_version` is `1` only when the Linux
|
|
managed runtime and protected library-bundle store are both constructed.
|
|
macOS and a Linux runtime without that store report `0`.
|
|
|
|
`refinement-eval-v1` plans require a strict `refinement` binding and exactly two
|
|
library projections: the fixture and the selected base or candidate skill. The
|
|
binding commits the fixture, base, and candidate projection IDs, SHA-256
|
|
digests, and destination aliases, the selected variant, and a matched-context
|
|
digest. Path grants are unavailable in V1. Missing bindings or an attempt to
|
|
fall back to the resource receipt V1 are rejected.
|
|
|
|
The fixture projection is canonical JSON using
|
|
`glove.refinement-eval-fixture-v1`. Unknown fields, duplicate/noncanonical
|
|
encoding, invalid UTF-8, unsafe bounds, or a projection/session/variant mismatch
|
|
fail before child release. The fixture binds:
|
|
|
|
- evaluation run, matched pair, session, and base/candidate variant;
|
|
- proposal, base projection, candidate projection, fixture, and dataset
|
|
digests;
|
|
- fixture ID, dataset reference, deterministic seed, model identity, harness,
|
|
and optional module identity/selectors;
|
|
- the selected skill projection;
|
|
- expected termination and exit code, optional maximum wall latency, and
|
|
canonical sorted required/forbidden UTF-8 transcript literals.
|
|
|
|
Remote configuration is mutually exclusive with local materialization and
|
|
Apple Container configuration. Startup prepares only mode-0600 SSH config and
|
|
known-host artifacts beneath the verified mode-0700 runtime directory. It does
|
|
not contact the configured host. Remote policy acceptance therefore remains
|
|
separate from launch authority.
|
|
|
|
## Cloudflare OS development template
|
|
|
|
`tests/fixtures/cloudflare-os-session-policy.json` is the checked-in,
|
|
owner-authored policy template for `runtime_id: "cloudflare-os"` and
|
|
`runtime_template_id: "cloudflare-os-dev"`. It uses
|
|
`remote_linux_container`; it is not a native Agent Skills adapter and does not
|
|
extend the closed native-adapter table. The plan selects the template ID and
|
|
its `adapter_command_digest`. The executable path, argument, environment, and
|
|
immutable closure remain operator-local policy fields.
|
|
|
|
The template commits `/opt/glove/runtime/cloudflare-os/node-v25/bin/node`, the
|
|
single fixed absolute argument
|
|
`/opt/glove/runtime/cloudflare-os/closure/scripts/run-local.mjs`, an empty
|
|
environment, and the read-only `/opt/glove/runtime/cloudflare-os/closure`. The
|
|
script is therefore selected from the immutable closure even when the writable
|
|
workspace contains a shadow `scripts/run-local.mjs`. The checked digest is valid
|
|
only for those exact fields. An operator changing any path or launch field must
|
|
recalculate it with the existing canonical `runtime_launch_template_digest`
|
|
implementation and revalidate the owner-only policy; a plan cannot override any
|
|
launch field. Before use, the owner must provision or replace the fixed
|
|
workspace and `/opt/glove` paths, protect the policy as mode `0600`, and
|
|
revalidate it. Provisioning the pinned Node, pnpm, workerd, or source closure is
|
|
outside this artifact and must not be performed by a session plan.
|
|
|
|
The only workspace mode is a quota-bounded 4 GiB `ephemeral_write` copy at
|
|
`/workspace`, removed after the session. Read binds, `direct_write`, and
|
|
`retained_write` are not authorized. `cfos-small` fixes 120 seconds of CPU,
|
|
2 GiB memory, 256 PIDs, five minutes wall time, 6 GiB disk, and 16 MiB terminal
|
|
output. `max_plan_ttl_ms` preserves the required one-second launch headroom.
|
|
No library projection or secret handle is authorized.
|
|
|
|
The named `cloudflare-os-loopback` egress policy contains exactly
|
|
`127.0.0.1:8787` with `allow_private: true`. No wildcard, non-loopback address,
|
|
or extra inspector port is present. This policy records the intended target;
|
|
network-namespace routing to the authenticated per-run proxy remains part of a
|
|
future operational remote lifecycle.
|
|
|
|
Loading or accepting this template does not make remote lifecycle operational.
|
|
The remote runtime still reports `lifecycle_operational() == false`, so
|
|
`start_session` and every other lifecycle method remain unadvertised and return
|
|
`method_not_found`. Policy and plan acceptance are construction-only until a
|
|
separate implementation supplies and tests the remote enforcement lifecycle.
|
|
|
|
Glove derives a plan-context digest from runtime, adapter, backend, network/tool
|
|
policy IDs, secrets, six resource limits, policy revision, and both skill
|
|
projection commitments. It excludes expiry and the selected variant/projection.
|
|
The normalized fixture context excludes only session, variant, and selected
|
|
skill projection. Their combined matched-context digest therefore permits only
|
|
the intended variant/session/expiry/skill-projection deltas across a matched
|
|
pair.
|
|
|
|
The evaluator consumes the complete online PTY stream, independently of the
|
|
bounded circular attach transcript. It never parses model output as a result
|
|
frame. The synthesized metrics are `passed`, `latency_us`,
|
|
`failed_assertions`, `required_literals`, and `forbidden_literals`. Token counts
|
|
and tool/security/egress event assertions are intentionally absent because the
|
|
current managed-session architecture does not expose those as trusted
|
|
session-bound observations.
|
|
|
|
The distinct authenticated refinement receipt carries the exact projections,
|
|
variant, matched context, synthesized outcome and commitment, complete raw-PTY
|
|
digest, resource receipt, and evaluator completeness. It shares the durable
|
|
journal sequence with resource receipts but has separate receipt/envelope HMAC
|
|
domains and a distinct registry transition. Torn-tail recovery, exact paging,
|
|
replay rejection, and terminal reconciliation apply to both receipt kinds.
|
|
|
|
`max_plan_ttl_ms` must exceed every resource profile's `wall_time_ms` by at
|
|
least one second. That headroom is reserved for approval and launch; Glove and
|
|
Sage reject a policy that cannot launch its own resource profiles. The guided
|
|
generator uses a ten-minute plan lifetime. It emits a two-minute `small`
|
|
profile for probes and short turns plus an explicit five-minute `interactive`
|
|
profile for model-backed turns that may spend most of their lifetime waiting on
|
|
audited egress.
|
|
|
|
`agent_runtime_adapter_schema_version` is independent of the raw process/PTY
|
|
lifecycle. It is `1` only for an active Linux managed-session runtime: Glove
|
|
creates a fresh private harness home, expands verified Sage `skill` bundles
|
|
into that adapter's native Agent Skills directory, and commits the derived
|
|
projection digest, home mount, and managed environment to the launch profile.
|
|
The authenticated terminal receipt is bound to that profile digest. Sage
|
|
requires version `1` before remote launch.
|
|
|
|
Glove images do not bundle agent harnesses or their credentials. A runtime
|
|
template may instead name bounded `launch.read_only_paths` for immutable files
|
|
belonging to an operator-installed harness (such as its package directory).
|
|
Glove mounts those paths read-only; Sage selects only the template ID and its
|
|
digest, never an executable or a host path. Runtime paths cannot be `/`,
|
|
relative, duplicate, or overlap a pinned executable.
|
|
|
|
Credential policies name exact owner-protected source files, but controller
|
|
plans contain only opaque handles. Linux launch imports each source into a
|
|
content-versioned Glove-owned lease, serializes use with an exclusive lock,
|
|
and mounts the lease writable at the adapter's exact path in its private home.
|
|
This supports vendor token rotation without granting the sandbox access to the
|
|
operator's source file or broader harness home.
|
|
|
|
For a supported native-skill runtime on Linux, an operator can replace a
|
|
pinned `executable_path` with a matching `runtime_discovery` value and an empty
|
|
executable path. Supported values and their Glove-owned private locations are:
|
|
|
|
| Runtime | Executable | Skill directory | Managed environment |
|
|
| ------------- | ---------- | ------------------------------------- | ------------------------------------- |
|
|
| `codex` | `codex` | `/home/agent/.codex/skills` | `CODEX_HOME=/home/agent/.codex` |
|
|
| `claude-code` | `claude` | `/home/agent/.claude/skills` | — |
|
|
| `pi` | `pi` | `/home/agent/.pi/agent/skills` | — |
|
|
| `copilot` | `copilot` | `/home/agent/.copilot/skills` | `COPILOT_HOME=/home/agent/.copilot` |
|
|
| `opencode` | `opencode` | `/home/agent/.config/opencode/skills` | `XDG_CONFIG_HOME=/home/agent/.config` |
|
|
|
|
Discovery requires a non-empty ordered `executable_search_paths` list. Each
|
|
directory is an explicit, digest-bound local policy value; Glove rejects any
|
|
directory or ancestor that is not owned by the service account or root, or is
|
|
writable by another principal. Glove never searches its inherited `PATH`. It
|
|
canonicalizes and identity-pins the selected file before execution. The adapter
|
|
ID, explicit search paths, arguments, environment, and read-only dependencies
|
|
are all covered by the local adapter digest. A Sage plan cannot select a
|
|
directory, binary, arguments, adapter, private-home path, or managed
|
|
environment. Discovery is rejected when it does not match the template's
|
|
runtime ID and fails closed when no executable is installed. Linux-production
|
|
templates may enter the authenticated managed-session lifecycle. macOS
|
|
templates use the same local discovery boundary only for SBPL-contained local
|
|
execution and remain experimental: Glove does not advertise resource
|
|
enforcement or remote managed-session capability there.
|
|
|
|
Package-manager bin directories are often group-writable. Explicit setup
|
|
discovery may inspect one, but it is not launch authority. `policy stage`
|
|
content-addresses and copies a supported unsafe single-root package/interpreter
|
|
closure into a dedicated protected runtime directory before the normal launch
|
|
resolver admits it. The protected snapshot—not the Sage plan or original
|
|
package-manager tree—remains the authority for the selected harness.
|
|
|
|
The owner-local workflow is programmatic:
|
|
|
|
```sh
|
|
glove policy detect --search-path /absolute/directory --json
|
|
glove policy stage --runtime codex --source /absolute/codex \
|
|
--directory /absolute/protected/codex --dry-run
|
|
glove policy stage --runtime codex --source /absolute/codex \
|
|
--directory /absolute/protected/codex --yes
|
|
glove policy generate --runtime codex \
|
|
--executable /absolute/canonical/codex --template-id codex-safe
|
|
glove policy explain --file /absolute/session-policy.json --json
|
|
glove policy validate --file /absolute/session-policy.json
|
|
```
|
|
|
|
`policy generate` emits one strict `runtime_templates[]` JSON entry after
|
|
canonicalizing either a pinned `--executable` or explicit `--search-path`
|
|
roots, resolving the adapter executable, and calculating its digest.
|
|
Arguments, canonical environment, read-only dependency paths, allowed aliases,
|
|
and projection destinations are repeatable explicit flags. `policy stage`
|
|
never reads or copies a harness home and never overwrites an existing entry
|
|
point. Its report includes the content digest when package/interpreter closure
|
|
copying was required. Pinned mode is the portable default for a Linux service in an
|
|
unprivileged user namespace: unmapped host-root ancestors retain an ambiguous
|
|
overflow UID and discovery continues to reject them rather than assuming they
|
|
were root-owned.
|
|
|
|
`direct_write` remains a legacy parse-only value and is rejected at launch.
|
|
New write-capable plans use isolated `retained_write` materializations; applying
|
|
their changes to the source is a separate, not-yet-enabled operation.
|
|
On Linux, retained copies use a hard-sized ext4 image attached through an
|
|
autoclear loop device so the staged filesystem survives daemon and host
|
|
restarts. Ephemeral copies and session scratch remain quota-sized tmpfs mounts.
|
|
Retained images require a root-owned, non-writable `/usr/sbin/mkfs.ext4` or
|
|
`/sbin/mkfs.ext4`, loop-control access, and mount capability.
|
|
|
|
## Protected files
|
|
|
|
| Object | Required properties | Bound |
|
|
| --------------------------------------- | ------------------------------------------------------------------------------ | ------------------------- |
|
|
| Runtime and materialization directories | absolute, current-user owned, mode `0700` | dedicated paths |
|
|
| Policy | regular file, current-user owned, mode `0600`, one hard link, no final symlink | 1 MiB |
|
|
| Session store | parent mode `0700`; regular file mode `0600`; one hard link; exclusive writer | 64 MiB and 10,000 records |
|
|
| Bundle root | directory mode `0700`, identity-pinned | digest filenames only |
|
|
| Bundle | regular file mode `0600`, one hard link, no symlink | 16 MiB |
|
|
|
|
Policy and bundle reads use one descriptor and reject metadata or identity
|
|
changes during validation. Policy is frozen for the process lifetime; replace
|
|
it atomically and restart `gloved` to change it.
|
|
|
|
## Dynamic path exposures
|
|
|
|
Exposure policy defines owner-approved roots, exact mode/quota tuples, maximum
|
|
TTL, and runtime templates. The paired journal records local descendant leases.
|
|
Both files require owner-only placement; the journal is bounded, hash-chained,
|
|
single-writer, and replayed strictly.
|
|
|
|
On Linux, descendants resolve with `openat2` using `RESOLVE_BENEATH`,
|
|
`RESOLVE_NO_SYMLINKS`, `RESOLVE_NO_MAGICLINKS`, and `RESOLVE_NO_XDEV`.
|
|
Other platforms use component-wise no-follow descriptor traversal. A catalog
|
|
entry exposes only its ID, generation, scope digest, label, allowed modes,
|
|
expiry, runtimes, and state. Paths, root IDs, and source identities remain
|
|
local. Replacing a live generation requires revocation or expiry.
|
|
|
|
`glove init <project>` is the owner-local enrollment client. It canonicalizes
|
|
the project path and requests one exposure beneath a root created by
|
|
`glove setup --path-root`; remote session requests cannot invoke enrollment or
|
|
supply a host path.
|
|
|
|
## Policy schema
|
|
|
|
The decoder rejects unknown fields. This example defines one runtime, one
|
|
ephemeral workspace grant, one library destination, and one mandatory resource
|
|
profile:
|
|
|
|
```json
|
|
{
|
|
"schema_version": 1,
|
|
"revision": 7,
|
|
"max_plan_ttl_ms": 120000,
|
|
"runtime_templates": [
|
|
{
|
|
"runtime_template_id": "codex-safe",
|
|
"runtime_id": "codex",
|
|
"adapter_command_digest": "05a49649e7973f6f8d6b119c9d525472517e6021fb38f8b191e0b40c8c4741d0",
|
|
"sandbox_backend": "linux_production",
|
|
"allowed_path_aliases": ["workspace"],
|
|
"allowed_projection_destinations": ["libraries"],
|
|
"launch": {
|
|
"executable_path": "/usr/bin/true",
|
|
"arguments": ["--version"],
|
|
"environment": ["PATH=/usr/bin:/bin", "TERM=xterm-256color"],
|
|
"read_only_paths": ["/opt/operator-harness/codex-runtime"]
|
|
}
|
|
}
|
|
],
|
|
"path_aliases": [
|
|
{
|
|
"alias": "workspace",
|
|
"host_path": "/srv/work/sage-protocol",
|
|
"target_path": "/workspace",
|
|
"max_ttl_secs": 120,
|
|
"access": [
|
|
{
|
|
"access": "ephemeral_write",
|
|
"materialization": "copy",
|
|
"create_policy": "empty_directory",
|
|
"cleanup_policy": "remove",
|
|
"max_bytes": 2097152
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"library_projection_destinations": [
|
|
{
|
|
"alias": "libraries",
|
|
"target_path": "/opt/sage/library-bundles"
|
|
}
|
|
],
|
|
"resource_profiles": [
|
|
{
|
|
"profile_id": "small",
|
|
"cpu_time_ms": 1000,
|
|
"memory_bytes": 67108864,
|
|
"pids": 16,
|
|
"wall_time_ms": 2000,
|
|
"disk_bytes": 2097152,
|
|
"terminal_output_bytes": 1048576
|
|
}
|
|
],
|
|
"egress_policy_ids": ["no-network"],
|
|
"tool_policy_ids": ["sage-readonly"],
|
|
"secret_handles": ["codex-token"]
|
|
}
|
|
```
|
|
|
|
A submitted plan must match:
|
|
|
|
- policy revision, expiry window, runtime and adapter digest;
|
|
- sandbox backend and all six numeric resource limits;
|
|
- egress, tool-policy, and secret identifiers;
|
|
- path aliases, access modes, materialization, cleanup, TTL, and byte quotas;
|
|
- library digests and configured destination aliases.
|
|
|
|
Retained changes are staged and inspected separately from host mutation. The
|
|
apply authorization schema, root-controlled Ed25519 trust-policy loader,
|
|
durable single-use reservation journal, and Linux whole-entry atomic-exchange
|
|
primitive are present. The primitive rejects stale baselines, copies without
|
|
following links, recursively syncs the candidate, uses
|
|
`renameat2(RENAME_EXCHANGE)`, and retains the old source for recovery. No
|
|
production signature backend or privileged signing helper is wired. Receipt
|
|
construction, reconstructable durable terminal finalization, prepared-candidate
|
|
discard, committed-state recovery, and idempotent baseline cleanup are
|
|
implemented locally. Grant IDs, authorization digests, and manifest digests are
|
|
each globally single-use. Ambiguous states remain consumed and non-terminal. These
|
|
operations are not yet connected to the control server, receipt audit stream,
|
|
or startup recovery sweep. Glove therefore advertises apply schema version `0`
|
|
and rejects apply operations.
|
|
|
|
Apply supports only service-owned, single-link, non-sparse regular files and
|
|
directories without ACLs, extended attributes, symlinks, special files, or
|
|
special mode bits. Contents and child modes come from the frozen stage; the
|
|
source-root mode is retained; ownership is normalized to the Glove service
|
|
identity; timestamps are intentionally regenerated. A production deployment
|
|
must also prevent Sage from mutating the source parent during the final
|
|
identity-check/exchange interval. Same-UID access to that parent is not a
|
|
sufficient boundary.
|
|
|
|
The pre-reservation and execution checks also require the full logical staged
|
|
tree plus a 64 MiB reserve to fit in the source filesystem's currently
|
|
available blocks. Copy bounds and filesystem errors still fail closed if
|
|
concurrent disk use consumes that headroom.
|
|
|
|
Arrays must be canonical and unique. Host paths and sandbox targets remain local
|
|
to the policy.
|
|
|
|
## Durable identity
|
|
|
|
Session creation stores the canonical plan and two digests:
|
|
|
|
- `controller_plan_digest`: Sage's 64-hex BLAKE3 correlation digest;
|
|
- `plan_content_digest`: Glove's SHA-256 digest of the exact canonical JSON.
|
|
|
|
A byte-identical retry with the same idempotency key returns the original
|
|
record. Reusing a session identity or key with different content fails closed.
|
|
Recovery rejects incomplete, corrupt, reordered, duplicate, or externally
|
|
resized records.
|
|
|
|
The lifecycle is:
|
|
|
|
```text
|
|
created → preparing → starting → running → stopping → exited
|
|
└──────────────────────────→ failed
|
|
```
|
|
|
|
Transitions are append-only and idempotent. Start authorization is short-lived
|
|
and bound to the session and both plan digests.
|
|
|
|
## Filesystem and launch binding
|
|
|
|
Path aliases are opened component-by-component without following links.
|
|
Restricted roots, overlapping aliases, non-canonical targets, and unsupported
|
|
file types are rejected. Resolved grants retain descriptor identity rather than
|
|
exposing a public raw host path.
|
|
|
|
Bundle requests contain a lowercase SHA-256 digest and destination alias. Glove
|
|
opens the digest-named file relative to the pinned root, rehashes it, and mounts
|
|
it read-only at the configured target. The launch binding and authenticated
|
|
terminal receipt commit each projection's identifier, destination, target, and
|
|
digest.
|
|
|
|
Runtime templates bind either an absolute executable or a constrained built-in
|
|
native-adapter discovery surface, plus ordered arguments and canonical environment, to
|
|
`adapter_command_digest`. Sage and Glove derive that digest independently.
|
|
Linux preparation then binds the resolved launch to cgroup identity,
|
|
quota-backed filesystems, the six limits, and resolved projections.
|
|
|
|
For `runtime_id: "codex"`, Glove never uses an image-bundled client or an
|
|
operator's existing Codex home. It creates `/home/agent`, injects
|
|
`HOME=/home/agent` and `CODEX_HOME=/home/agent/.codex`, and materializes only
|
|
the exact verified `skill` entries requested by the Sage plan. The operator
|
|
must still locally configure either the executable or Codex discovery paths,
|
|
plus any immutable dependencies, through the launch template. Network
|
|
credentials and egress remain separate policy features and are not exposed by
|
|
this adapter.
|
|
|
|
On macOS, `container_macos_codex_discovery` is a local live probe. When Codex
|
|
is present on the `gloved` service PATH, it resolves the canonical executable
|
|
through the policy and runs `codex --version` under `sandbox-exec`; otherwise
|
|
CTest marks the probe skipped. `scripts/testing/glove_local_harness.sh` runs
|
|
that lane alongside the filesystem and environment SBPL probes. Linux keeps
|
|
the privileged Docker workflow lane for namespace, cgroup, receipt, cleanup,
|
|
and no-network assertions.
|
|
|
|
## Guest observation channels and Sage guest proposals
|
|
|
|
The guest service channel is schema-generic: Glove core enforces structural
|
|
invariants (identifier charset, digest hex, TTL/skew arithmetic, capacity,
|
|
idempotency) and delegates body semantics to the host-registered admission
|
|
table. Schemas are registered by the host at registry construction, never
|
|
compiled into core. Current admission and enqueue fail closed for an
|
|
unregistered schema. Recovery may retain an authentic historical record after
|
|
its schema retires only under the metadata-only quarantine rule below.
|
|
|
|
The Sage guest adapter registers only the bounded observation schema required
|
|
for current registry admission. Dormant proposal and mutation schemas remain
|
|
unregistered until an authenticated activation path exists, and current enqueue
|
|
rejects every unregistered schema. Only authentic records already present in
|
|
durable history may recover after schema retirement; they become non-actionable
|
|
quarantine metadata and cannot be enqueued again or launched.
|
|
|
|
Linux local-service forwarding is enabled when operator configuration has a
|
|
runtime-allowlisted endpoint. Configuration contains only a canonical sorted
|
|
alias list, normalized absolute host Unix-socket paths, canonical sorted runtime
|
|
IDs, and an optional opaque guest-channel adapter/schema binding; guest targets,
|
|
TCP, UIDs, tokens, credentials, and payload semantics are not configurable.
|
|
Glove core does not interpret that binding. The explicit Sage adapter resolves
|
|
only its exact binding and runtime set; an unsupported binding fails startup,
|
|
while an unbound arbitrary service—including one allowed for `pi`—remains a
|
|
generic proxy and advertises no Sage channel capability. Codex-only and
|
|
OpenCode-only endpoint sets likewise advertise schema version `0`.
|
|
|
|
Each admitted session receives a read-only directory at
|
|
`/run/glove-services/local` and only
|
|
`GLOVE_LOCAL_SERVICE_DIR=/run/glove-services/local`. Alias sockets are `0600`;
|
|
their owner-`0700` directory and the configured upstream parent remain
|
|
descriptor-pinned. Endpoint pathname identity is checked before and after
|
|
connect, but portable pathname `AF_UNIX` does not exclude same-UID ABA
|
|
replacement. The operator and other processes under the service UID are trusted
|
|
endpoint authority. Linux `SO_PEERCRED` must match the configured UID at both
|
|
guest and upstream connections; this detects drift and mismatched peers, not
|
|
application-process identity.
|
|
|
|
Before any path materialization, Glove compares each resolved grant descriptor
|
|
with the descriptor-pinned endpoint parent and every actual ancestor descriptor. A grant
|
|
for the socket, its parent, or an ancestor that would expose it is rejected;
|
|
lexical aliases do not bypass this identity check. The nested user-namespace
|
|
credential lane remains a Linux privileged environment gate.
|
|
|
|
One opaque request and response, each at most 16 KiB, share one absolute
|
|
deadline. Before release, Glove durably records non-success
|
|
`delivery_pending`; it records `delivered` only after a successful guest send,
|
|
and best-effort `delivery_failed` after a failed send. Records contain only the
|
|
session ID, alias, phase, status, and latency. Audit appends check the same
|
|
deadline before and after synchronous durability work. A kernel-blocked
|
|
`fsync(2)` cannot be preempted cooperatively, so this is not a hard wall-clock
|
|
bound; if it completes after the deadline, the response remains suppressed and
|
|
the conservative pending record remains. Apple advertises no G6 capability.
|
|
|
|
## Receipts and recovery
|
|
|
|
Terminal resource receipts are HMAC-SHA-256 authenticated and chained to Sage's
|
|
trusted anchor. The producer reserves journal capacity before launch and
|
|
durably appends the terminal envelope before success. Delivery is paged and
|
|
acknowledgement must match the exact head; Sage archives before acknowledging.
|
|
|
|
Recovery identifies processes by boot ID, PID start time, and cgroup identity,
|
|
not PID alone. Identity mismatch has no signaling side effect. Orphaned
|
|
materializations are removed only after quota and descriptor checks. Retained
|
|
images are remounted and frozen before cleanup; volatile materializations lost
|
|
across a host restart are removed as empty mountpoints.
|
|
|
|
Reserved apply transactions can reopen the exact journaled exposure parent by
|
|
descriptor after lease revocation or expiry. This recovery-only lookup requires
|
|
the exposure ID, generation, scope digest, and original source-identity digest;
|
|
the local exposure journal also binds the parent directory identity, so a
|
|
replacement at the same path is rejected. The lookup exposes neither a raw path
|
|
nor launch authority.
|
|
|
|
Recovery classifies only four descriptor-proven states: reserved, prepared,
|
|
committed, or ambiguous. Reserved and safely discarded prepared transactions
|
|
receive durable failed receipts. Committed exchanges receive durable applied
|
|
receipts before baseline cleanup. The journal stores the stable failure code
|
|
and all receipt bindings, so either terminal receipt can be reconstructed after
|
|
restart. Failed terminal receipts require no surviving stage or exposure
|
|
locator. Applied terminals resolve those objects only to retry baseline
|
|
cleanup. The bounded startup reconciler reports redacted per-record issue
|
|
codes. Ambiguous transactions are never retried or terminalized automatically.
|
|
|
|
## Remaining boundary
|
|
|
|
Glove's native adapters expand only exact `skill` bundle entries into a
|
|
private-home `SKILL.md` layout. `behavior`, `prompt`, `template`, and
|
|
`workflow` are admitted only as known non-skill entries: they remain in the
|
|
verified raw bundle and make that raw bundle a required launch input rather
|
|
than being interpreted as skills. Apple Container rejects any projection
|
|
that contains a known non-skill entry because its launch path cannot preserve
|
|
the required raw bundle. Unknown kinds fail closed. Harnesses do not inherit
|
|
Codex's layout, and `prompt_ref` remains rejected. Live direct host writes
|
|
are outside v2.
|
|
Applying a retained stage remains unavailable until Glove verifies an
|
|
independently signed, single-use local authorization.
|