Pull from Another asz

An asz that collects a product’s agents serves its conversations. On your own machine, asz pull copies the ones you choose, so you can judge them and read them here. A copy is a snapshot of the conversation’s head, checked against the digests its rounds carry. It never changes, and it expires.

How it is deployed

Set up as below, the server answers pulls over HTTPS, where TLS encrypts the connection, and a token goes with every request. Both are set on the server, as Tokens and TLS says.

 Where the agents run, in a private network
┌────────────────────────────────────────────────────────────────────────────────┐
│                                                                                │
│  agents ──── traces, sent over the network ────────────►  asz server           │
│     │                                                     collects what it is  │
│     └── transcripts and file changes, written ──┐         sent and what it     │
│         to a shared volume                      └──────►  finds on the volume, │
│                                                           into its storage     │
│                                                           root                 │
│                                                                                │
│                                     one address, 8788: the page and the pull   │
│                                     routes, a token on every request, TLS      │
└──────────────────────────────────────────────┬─────────────────────────────────┘
                                               │  HTTPS, encrypted by TLS
                                               │  Authorization: Bearer TOKEN
                                               │  a client certificate, when asked
┌──────────────────────────────────────────────┴─────────────────────────────────┐
│ Your machine                                                                   │
│                                                                                │
│  asz pull -list                           what the server holds                │
│  asz pull prod/CONVERSATION               a snapshot under                     │
│                                           _remote/prod/CONVERSATION/TAG/       │
│  asz judge -remote-conversation           judges a snapshot here               │
│      prod/CONVERSATION                                                         │
│  asz view                                 pulled conversations beside your own │
└────────────────────────────────────────────────────────────────────────────────┘

The server is the asz that already collects the agents’ conversations: one process, asz server, or asz view beside a collector. It answers pulls on the same address as its page. A load balancer or a proxy may stand between the two ends; a shared cache there never answers a request that carries a token.

Serve pulls

The server needs a certificate and its key, in PEM, for the host name your machine will use in the url. If your own authority signs it, rather than one the system trusts, each machine that pulls needs that authority’s certificate too.

Make a directory the user that runs asz owns, and a token for each person or machine that pulls:

sudo install -d -o "$USER" -m 700 /etc/asz
asz token create -file /etc/asz/tokens alice

It prints the token once, on the line that starts with token , and adds the token’s SHA-256 to the tokens file. Give the token, the text after token , to Alice. To revoke it, delete its line: the server reads the file again within five seconds.

The server’s asz.yaml:

storage:
  root: /var/lib/asz/data
remote_server:
  tokens_file: /etc/asz/tokens
  tls:
    cert_file: /etc/asz/server.crt
    key_file: /etc/asz/server.key

Then serve, with asz server or asz view, on an address others can reach:

asz server -config /etc/asz/asz.yaml 0.0.0.0:8788

With a tokens file, every request needs a token, the page and the requests it makes too. One process serves a root’s pulls at a time: a second asz view on the same root serves the page and waits to take over the pulls. Configuration lists every key.

In a container, keep the storage root on a volume. A directory shared from a macOS host into Docker Desktop can fail asz’s checks of its own directories.

Pull

On your machine, save the token in a file only you can read. Paste it, then press Ctrl-D:

mkdir -p ~/.asz && chmod 700 ~/.asz
(umask 077 && cat > ~/.asz/prod.token)

Name the server in asz.yaml:

storage:
  root: ~/asz-data
remotes:
  - name: prod
    url: https://asz.example.internal:8788
    token_file: ~/.asz/prod.token
    # Only when your own authority signed the server's certificate.
    ca_file: ~/.asz/ca.crt

List what it holds, newest activity first:

asz pull -list
asz pull -list -remote prod -since 24h -match parser

LOCAL is the head round of the newest unexpired snapshot here that admits a reader. Only the snapshot’s own records are read for it, not its files. A snapshot whose records do not read is said and passed over to the next older one. ? means the conversation’s copy here is damaged itself, its own records or its directories, and makes the command exit 1. Each -list prints one page. When a remote has more, the last line gives the command for the next page, with the same filters.

Pull one or more conversations, by the identifier the listing shows after prod/:

asz pull prod/7722b6a5-2f0b-4a7e-9a8b-4b4f1e9f6c11

It prints each pull’s snapshot as prod/CONVERSATION:TAG, and unchanged after it when the newest unexpired snapshot here already holds the same files under the same head. A pull reuses what the newest valid snapshot here holds, and fetches the rest. It exits 0 when every pull succeeded, 1 when any failed, and 2 when its arguments are wrong. When one conversation fails, the next goes on. When the remote itself fails, such as a refused token or certificate, the remaining pulls from that remote stop, and the other remotes go on.

-list flag Meaning
-remote NAME List this remote only. Repeat for several. Default: every remote.
-since T, -until T Last activity at or after, or before: a duration back from now, such as 24h, or an RFC 3339 time.
-runtime R The session’s runtime, such as claude-code/1.
-match TEXT Text in the title, letter case ignored.
-min-talks N At least this many talks.
-limit N Rows per remote, 1 to 500. Default: 50.
-after CURSOR The next page, with exactly one -remote.

Tokens and TLS

Every request to a server with a tokens file carries a token: Authorization: Bearer TOKEN. A request with none, or with one that is not in the file, gets 401. The server keeps only each token’s SHA-256.

TLS encrypts the connection. Without cert_file and key_file the server speaks plain HTTP, and tokens travel in clear text; asz warns when that address is not a loopback one. With TLS, your machine always checks the server’s certificate: against the system’s authorities, or against ca_file when it is set. Set server_name when the certificate names another host than the one in url, as through a tunnel. asz follows no redirect, so a token never goes to another host, and it uses an http:// proxy from the environment, never an https:// one.

A token can come from a file, token_file, or from an environment variable, token_env: exactly one of the two.

To require a client certificate as well, set client_ca_file on the server:

remote_server:
  tokens_file: /etc/asz/tokens
  tls:
    cert_file: /etc/asz/server.crt
    key_file: /etc/asz/server.key
    client_ca_file: /etc/asz/clients-ca.crt

and give each machine that pulls its certificate and key, beside its token:

remotes:
  - name: prod
    url: https://asz.example.internal:8788
    token_env: ASZ_PROD_TOKEN
    ca_file: ~/.asz/ca.crt
    client_cert_file: ~/.asz/client.crt
    client_key_file: ~/.asz/client.key

A machine without a certificate that authority signed is refused before any request is answered, and its pull stops.

Judge a pulled conversation

Judging needs an evaluator, as it does for a local conversation: by default the claude program, installed and signed in. Judge says how to use another.

Give the pulled conversation in place of a conversation identifier:

asz judge -remote-conversation prod/7722b6a5-2f0b-4a7e-9a8b-4b4f1e9f6c11
asz judge -remote-conversation prod/7722b6a5-2f0b-4a7e-9a8b-4b4f1e9f6c11:20261025T103200.123456789Z
asz judge -pull -remote-conversation prod/7722b6a5-2f0b-4a7e-9a8b-4b4f1e9f6c11

Without a tag, or with :latest, it judges the newest unexpired snapshot here, and fails when that snapshot is damaged. Without -pull nothing is asked of the server, so a remote no longer configured does not stop you judging what is here. A run lives in the snapshot it judged.

-pull pulls the head first and judges what it pulled, as a new run. So it takes no fixed tag, though :latest is allowed, and no -list, -show or -regroup, which select runs already made. Every other judge flag works as it does on a local conversation.

-list without a tag lists the runs of every unexpired snapshot, each as prod/CONVERSATION:TAG run 3. -show and -regroup take a run’s number within the selected snapshot.

Read a pulled conversation

asz view and asz server list the conversations pulled into their root beside their own, each from its newest unexpired snapshot. Its title starts with [remote, prod].

Where copies live

Copies of conversation CONVERSATION from remote prod live under _remote/prod/CONVERSATION/ in the storage root, one directory per snapshot, named by its tag. A snapshot and every judge run in it expire remote_snapshot_ttl after it was published, 720h by default. asz pull and asz judge -remote-conversation remove the expired snapshots they find when they start. They leave one a reader still holds, one whose records do not read, and every snapshot of a conversation another command holds at that moment. Collection, parsing and sending refuse a snapshot as their root. Judging writes inside one, a run in its own directory. asz.pull describes what travels between the two.