> ## Documentation Index
> Fetch the complete documentation index at: https://docs.attesso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosted sync and policy

> Mirror decisions to the workspace, publish a signed rule set, and know exactly what leaves the machine.

The local ledger stays the source of truth. Hosted sync mirrors the next decisions to the workspace so the record can be followed from any machine, and hosted policy lets an administrator publish one rule set that every connected machine verifies before adopting.

Both need an API key that belongs to an entitled live workspace. The free local tier needs no connection.

## Configure the workspace

```sh theme={"theme":"github-light"}
export ATTESSO_SYNC_URL=https://api.attesso.com
export ATTESSO_API_KEY=att_live_...
```

Resolution order: explicit `--url` and `--key`, then `ATTESSO_SYNC_URL` and `ATTESSO_API_KEY`, then `~/.config/attesso/config.json` (mode 0600). The local UI writes that file through **Connect workspace**; `sync` and `policy pull` read it. The key is never printed back.

The URL must be `https`, except on loopback where `http` is allowed for local testing, and it may not contain credentials. A key must look like an Attesso API key; anything else is rejected before a request is made.

## Sync

```sh theme={"theme":"github-light"}
npx attesso sync
npx attesso sync --dry-run
npx attesso sync --limit 100
npx attesso sync --watch
```

| Flag | Meaning |
| - | - |
| `--limit N` | rows per batch (decisions plus usage), 1 to 100; default 50 |
| `--dry-run` | count what is ready, send nothing, move no cursor |
| `--watch` | repeat the next batch every five seconds until `Ctrl-C`; `--interval MS` changes the period |
| `--json` | the server's `accepted`, `skipped`, `metered`, `usage`, `last_seq` result |

Each batch carries at most `limit` rows. A decision carries only `seq`, `occurred_at`, `action`, `subject`, `capability`, `decision`, `code`, `task` and `entry_hash`; a usage row carries `seq`, `occurred_at`, `task`, `model`, `input_tokens`, `output_tokens`, `cached_tokens` and `cost_usd`. Arguments, prompts, `meta` and amount stay on the machine. The device id is the ledger's key id; change the key and the cursor starts again rather than claiming someone else's history. Usage rows are mirrored but not metered.

The cursor lives in `.attesso/sync.json` and advances to the last sent seq only after the server answers with valid integers. A failed request leaves it alone, so the next run retries the same batch; the server deduplicates by device and sequence, which makes a retry safe. `accepted`, `skipped` and `metered` are what the workspace counted; `metered` is what it booked as billable, and `usage` is how many usage rows it stored.

## The hosted views

Once a workspace has synced a decision, the dashboard's Bounds entry opens the hosted overview at `/dashboard/<env>/bounds/hosted` directly. `/dashboard/<env>/bounds?setup=1` keeps the local setup instructions for a workspace that has not synced yet.

The overview shows the lifetime decision and refusal counts, device sync health, the refusal reasons and tool activity for the last 24 hours, the three newest open refusals, the five newest decisions, and the token card described below.

### Review queue

`/dashboard/<env>/bounds/reviews` lists every refusal, newest first, with filters for status (`open`, `resolved`, `ignored`, `all`), code and device. Closing an item records a resolution or an ignore on the control plane; it never changes the local verdict, the policy, the rules or the signed ledger. The overview previews the three newest open items and links here with the full count.

### Decisions

`/dashboard/<env>/bounds/decisions` lists every mirrored decision, newest first, with filters for decision (`all`, `ALLOW`, `DENY`), code, device and window (24 hours, 7 days, 30 days, all time). The refusal-reason links on the overview open this page filtered to that code. Use the decision filter to see only refused calls, and the window filter to bound a long history.

Both pages use a keyset cursor: **Older** continues exactly where the page ended, and the filters live in the URL, so a filtered view can be shared or reloaded. The overview requests three reviews and five decisions; the full pages request twenty rows per page.

### Token usage and the 24-hour summary

Usage is reported by the agent, never inferred from decisions. `attesso usage` records one measured model request against a task (for a hook, the session id):

```sh theme={"theme":"github-light"}
npx attesso usage --task SESSION --input 12000 --output 800 --cached 64000 \
  --cost 0.0042 --model MODEL
```

A harness hook can call it on every model response. Hermes delivers the usage under `extra`, so a one-line hook script records it without blocking the request:

```sh theme={"theme":"github-light"}
#!/bin/sh
npx attesso --dir ~/.hermes/attesso-veto/ledger4 usage --stdin --json >/dev/null 2>&1 || true
```

```yaml theme={"theme":"github-light"}
hooks:
  post_api_request:
    - command: /Users/you/.hermes/attesso-veto/usage-hook.sh
      timeout: 10
```

The 24-hour card leads with what needs a decision: refused calls, decisions, measured coverage and open reviews, all exact. The measured token total (input, output, sessions, reported cost) is one line below it.

No figure is inferred from decisions. With no reported usage the card says **no token usage reported** rather than showing a fabricated number.

## Publish a rule set

A workspace administrator authors the rule set at `/dashboard/<env>/bounds/policy`. Publishing creates an immutable version signed with Attesso's evidence key. The local engine fetches it, verifies the envelope against `/.well-known/jwks.json`, and only then replaces `.attesso/rules.json`:

```sh theme={"theme":"github-light"}
npx attesso policy pull
npx attesso policy pull --apply
npx attesso policy pull --url https://api.attesso.com --json
```

Without `--apply` the command verifies and previews the version and digest; with it, `rules.json` is written (or reported as already current). A tampered signature, an unknown key, an unexpected format or an invalid rule set writes nothing. A workspace with no published policy answers 404 and the command says so.

The bundle is `attesso.bounds.policy.v1`. Path scope and `shell_scope` are not in it: those stay on the machine that enforces them, set with `attesso paths --set`.

> [!NOTE]
>
> Local verification proves the bundle came from Attesso's signing key and was not edited in transit. Who inside the workspace published it, and whether they had the right to, is a control-plane question the local engine does not check; treat publishing rights as the authority.

## What hosted adds, and what it does not

| Adds | Does not add |
| - | - |
| a mirror of decisions the workspace can read from anywhere | new decision logic: the local engine judges the same way |
| a review queue over refusals, with filters and a full page | any change to the local verdict when an item is closed |
| a measured token usage mirror with a 24-hour decision summary | a savings or cost claim: refusals and decisions are exact, tokens are measured only |
| a signed, versioned rule bundle | path scope, shell scope or tool pins, which stay local |
| passkey approval and evidence a third party verifies offline | authority for a local-only ledger, which keeps proving integrity |

The split is the same one [Receipts and packets](https://docs.attesso.com/bounds/evidence.md) sets out: local proves the file was not edited, hosted proves a named human approved exact terms and lets someone who does not trust you check it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.