# ScopeWrit

[Live product brief](https://scopewrit.cyberelf.link/) ·
[Cyberelf Labs](https://cyberelf.link/)

ScopeWrit is a deterministic authorization boundary for autonomous agent runs.
A principal signs one exact scope manifest. An agent can propose any action it
wants, but a trusted adapter acts only after ScopeWrit verifies that the action,
resource, method, limits, time window, and run identity are inside that signed
authority.

The security claim is deliberately narrow:

> ScopeWrit can make reachability different from permission when every
> effectful path is forced through a guarded adapter.

It does not make a model aligned, infer permission from prose, or transparently
sandbox processes that still have raw network, shell, browser, or cloud access.

## What is implemented

- strict YAML manifests with duplicate-key rejection and bounded input size;
- Ed25519 signatures verified against operator-configured trust roots;
- one-run nonces, expiry, durable revocation state, and restart-safe manifests;
- deny-by-default rules with explicit-deny precedence;
- canonical resource matching, left-most DNS wildcards, private-address opt-in,
  resolved-CIDR checks, method constraints, attributes, and per-rule use limits;
- exact, short-lived, adapter-bound action tickets with consume-once replay
  protection;
- deterministic scope-uncertainty signals that pause for an exact,
  principal-signed approval;
- explicit HTTP egress, MCP, subprocess, browser, cloud, credential, package,
  and publishing adapter boundaries;
- an authenticated local control API with arbitrary HTTP egress off by default;
- an fsynced, hash-chained flight recorder and signed audit packet;
- an isolated guarded-versus-unguarded benchmark that never contacts the public
  Internet.

## The control path

```mermaid
flowchart LR
    P["Principal signs one run manifest"] --> V["Verify trust, time, nonce"]
    V --> A["Run attestation"]
    G["Agent proposes an action"] --> D["Deny-first policy decision"]
    A --> D
    D -->|DENY| L["Hash-chain flight recorder"]
    D -->|PAUSE| H["Exact human approval"]
    H --> D
    D -->|ALLOW| T["Short-lived action ticket"]
    T --> C["Trusted adapter consumes once"]
    C --> E["HTTP / MCP / shell / SDK effect"]
    E --> L
    L --> R["Signed audit packet"]
```

The action ticket is not a suggestion. It is bound to the run, manifest digest,
normalized resource, action, proposal digest, adapter class, and expiry. A
ticket cannot be replayed or moved to another adapter.

## Reproduce the evidence

Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/).

```powershell
uv sync --extra test
uv run ruff check src tests_py
uv run pytest
uv run scopewrit benchmark --output evidence/local/isolated
uv run scopewrit measure --output evidence/local/latency --iterations 500
uv build
```

The isolated benchmark gives the same deterministic goal-following control the
same synthetic briefing twice:

1. without a gate, it reaches the synthetic outside target and triggers the
   fake compromise;
2. through ScopeWrit, it reads the allowed briefing, attempts the same outside
   URL, is denied before network I/O, and can still read the allowed health
   endpoint.

The published local receipt is
[`evidence/isolated-benchmark-20260730/benchmark-report.json`](evidence/isolated-benchmark-20260730/benchmark-report.json).
The paired local latency receipt is
[`evidence/policy-latency-20260730/policy-latency.json`](evidence/policy-latency-20260730/policy-latency.json).
The runtime dependency audit checked 21 locked packages and reported zero known
vulnerabilities in
[`evidence/python-dependency-audit-20260730.json`](evidence/python-dependency-audit-20260730.json).
Bandit reported zero unsuppressed findings; the five reviewed exclusions and
remaining manual boundaries are recorded in
[`docs/SECURITY_AUDIT.md`](docs/SECURITY_AUDIT.md).

This benchmark proves adapter enforcement, not model behavior. The benchmark
agent is a deterministic control, not an LLM.

## Signed manifest

```yaml
schema_version: "1.0"
mandate_id: mandate-security-review-001
run_id: run-security-review-001
principal: cyberelf-labs
agent_id: research-agent-01
objective: Inspect the approved lab service without contacting any other host.
issued_at: 2026-07-30T20:00:00Z
not_before: 2026-07-30T20:00:00Z
expires_at: 2026-07-30T20:30:00Z
nonce: D3Xk7P2Qm9Va4Nt8C1Ls6R
rules:
  - rule_id: deny-admin
    effect: deny
    action: network.http
    resource: https://lab.example/admin/**
    methods: [GET, POST]
  - rule_id: allow-read-lab
    effect: allow
    action: network.http
    resource: https://lab.example/public/**
    methods: [GET]
    max_uses: 20
pause_on_scope_uncertainty: true
ticket_ttl_seconds: 30
claim_boundary: This run grants read-only HTTP authority to one public path.
```

Generate a principal key and sign the YAML offline:

```powershell
uv run scopewrit keygen `
  --private .scopewrit/principal.pem `
  --public .scopewrit/principal.pub.pem

uv run scopewrit sign `
  --manifest scope.yaml `
  --private-key .scopewrit/principal.pem `
  --key-id security-principal-2026 `
  --out scope.signed.json

uv run scopewrit verify-manifest `
  --signed scope.signed.json `
  --public-key .scopewrit/principal.pub.pem `
  --key-id security-principal-2026
```

The public key is a trust root. An embedded or substituted key is never trusted
merely because it can verify its own signature.

## Integration surfaces

- `GuardedHTTPClient` resolves once, authorizes the complete address set,
  then connects to the selected literal IP. It preserves the logical Host and
  verified TLS hostname, disables proxies/redirects/connection reuse, and records
  the connected IP and response digest. Cookies are not carried between calls.
- `GuardedMCPFirewall` binds a call to `mcp://server/tool` and hashes arguments
  before dispatch.
- `guarded_subprocess` requires an absolute executable and a principal-signed
  digest of the complete canonical argv, uses `shell=False`, and records exit
  and stream digests.
- `GuardedToolAdapter` fixes browser, cloud, credential, package, and publish
  SDK callbacks to their adapter class.
- `create_app(...)` exposes authenticated start, authorize, close, and optional
  HTTP-egress routes. Egress is disabled unless the operator explicitly enables
  it. The authenticated audit route retrieves the signed packet after closure.

Recorder writes use an OS file lock and verify the existing chain under that
lock. Closure refuses unresolved consumed tickets, is recoverable after an
audit-export failure, and rejects later writes. This supports cooperating
processes on one local filesystem; it is not a distributed consensus log.

The adapter process must be more trusted than the agent. Giving the agent the
raw SDK client, socket, browser session, or shell alongside the adapter defeats
the boundary.

## Evidence and limitations

The current local suite covers signature substitution and tamper, nonce and
ticket replay, expiry, revocation, adapter confusion, URL ambiguity, SSRF-style
private-address rules, deny precedence, use limits, exact approvals, MCP,
subprocess execution, API authentication, ledger tamper/truncation, restart,
and the guarded/unguarded scenario.

Read [STATUS.md](STATUS.md) for the measured release state and
[docs/THREAT_MODEL.md](docs/THREAT_MODEL.md) before deploying. ScopeWrit is a
working public alpha, not an independent security certification or a complete
host sandbox.

## License

AGPL-3.0-or-later. Commercial deployment and support are available from
[Cyberelf Labs](https://cyberelf.link/) at
[justin@cyberelf.link](mailto:justin@cyberelf.link).
