The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Zas listing page.
An MCP server that lets a coding agent — Claude Code, Codex, or anything that speaks MCP — send files and notes into your Zas channels, and read items back out of them.
Zas moves things between your devices and the people you choose: files, photos, text, code. You put something into a channel on one device and use it on another. Whatever you do not pin leaves on its own after a few days, so there is nothing to tidy up later. Everything is encrypted on the device before it is uploaded: the database and the object storage receive encrypted bytes, not filenames, content or previews.
This package is the piece that lets a coding agent use your channels the way your other devices do, under an identity of its own that you approve, that you scope to the channels you choose, and that you can revoke.
Your coding agent gets eleven tools. It can send a file or a note into a channel you picked, send or receive a file live through Directo, list what is in that channel, and pull one item back onto disk. Everything it sends is encrypted on your machine before it leaves, lands in your account, and is marked in the channel as sent by that agent. The agent has an identity of its own and never holds your account key: pairing mints a key pair here, you approve it from the web app, and from then on it signs a challenge to get a short-lived session.
Install Node.js LTS (22 or newer) and your agent's command-line tool first. Reopen your terminal after installing. Run these commands on the computer where you use the agent.
Choose your agent and pair it. On Windows (PowerShell or CMD):
Codex
Claude Code
Open the printed link, approve the channels, and enter the code in the terminal if the page shows one. Leave the terminal open until pairing finishes. Then run the matching registration command:
Codex
Claude Code
On macOS, Linux, or WSL, use npx in place of npx.cmd and omit
cmd /d /c . Pair and register in the same environment. Restart your agent
and ask it to send something to Zas.
If a command is not recognized, install Node.js or the named agent's CLI and
reopen the terminal. The first download needs internet access and may take a
few minutes. Use @latest as shown so npm requests the published package
instead of selecting an unbuilt local workspace.
zas-agent pair mints the key pair, registers the public halves, opens the
approval page in your browser, and waits. The terminal shows:
Signed in, name the agent and tick the channels it may use — sending is the
default, reading is a separate switch — and approve. Approval creates
nothing by itself: the page hands a one-time claim code to this terminal
over 127.0.0.1 (the port in the link), and the agent exists only once the
terminal claims with that code and the secret it holds. A link that reached
somebody else is approved on their machine, where nothing listens, and
expires with nothing created.
If the browser cannot reach the terminal — the link was opened on a phone,
or the browser refused the local connection — the page shows the code and
the terminal asks for it. Type it there and nowhere else: with that code,
another terminal that started a pairing could claim it. Pass --no-open or
set ZAS_NO_OPEN=1 to keep the browser closed; the link is printed either
way. The pairing is good for ten minutes before approval and five after;
past that, run the command again.
You can also start the flow from the coding agent with the zas_pair tool:
the first call hands back the URL, a later call says whether the approval
landed, and if the page shows a code, a call with code claims with it.
Every line here is a fact. The ones about this package you can check in this repository. Where a property is enforced by the Zas server, which is not open source, the sentence says so.
zas-agent pair generates two key
pairs on your machine — X25519 to receive channel keys, P-256 to sign
sign-ins — and the private halves never leave it. The agent never holds your
account key, and the key-derivation service refuses account-key derivation to
an agent (server-side).>_
agent mark and the agent's name, in the channel, on every device you read Zas
from.src/shared/ are the
same modules the Zas web app ships. The server stores ciphertext and never
sees a channel key, a channel name or item plaintext (server-side).release.yml workflow in this repository, with npm
provenance, so the tarball on npm can be traced back to a commit and a
workflow run.The package refuses some of these on its own, before a request is made. The ones marked (server-side) are enforced by the Zas server.
read is a separate switch from
send; without it, zas_list_items and zas_get_item are refused.zas_send_direct, a live transfer that stores nothing.zas_receive_direct takes the same
read switch as listing. And it only ever runs inside a tool call: the
agent never watches your channels, so nothing arrives unasked.The agent answers in a closed set of codes. Anything a server route says that
is not in this set collapses to upload_failed or network, so no raw server
string ever reaches a terminal.
| Code | What it means |
|---|---|
not_paired | This agent is not paired yet. |
identity_corrupt | The identity file on disk is damaged. |
agent_revoked | The owner revoked this agent. |
agent_forbidden | Only the account owner can do that. |
grant_missing | This agent has no access to that channel. |
send_forbidden | This agent cannot send to that channel. |
read_forbidden | This agent cannot read that channel. |
direct_mode | That channel is in Directo mode. |
not_direct_mode | That channel is not in Directo mode. |
not_claimed | Nobody received the file within ten minutes; the offer was withdrawn. |
no_offer | Nobody offered a file through Directo while the call waited. |
offer_taken | Another device received that file first. |
direct_cancelled | The offer was cancelled from the other side. |
direct_failed | The Directo transfer failed in flight. |
direct_not_failed | That job is not a Directo transfer that failed in flight. |
file_changed | The file changed since the Directo offer. |
webrtc_unavailable | The WebRTC engine could not be loaded on this machine. |
fallback_unavailable | Reliable delivery is not available right now. |
key_stale | The channel key changed; the owner refreshes it by opening Zas. |
quota_exceeded | The account reached its storage limit. |
rate_limited | Too many sends in a row. |
file_too_big | The file is over the plan limit. |
duplicate | That item is already in the channel. |
not_found | That item is not in the channel. |
invalid_cap | That file is no longer available. |
write_failed | The download destination could not be written. |
pairing_expired | The pairing expired; pair again. |
pairing_cancelled | The owner cancelled the pairing. |
feature_disabled | Agents are not enabled for this account yet. |
upload_failed | The upload failed. |
oprf_failed | Zas did not answer correctly while preparing the file. |
network | Zas cannot be reached. |
sign_in_failed | Zas did not accept this agent session. |
bad_signature | Zas rejected this agent's signature; pair it again. |
missing_token | The session token is missing; pair the agent again. |
internal | Something failed inside the agent. |
Every code comes back as one sentence in English, never as a stack trace.
| Tool | What it does |
|---|---|
zas_status | Says whether this agent is paired with a Zas account, and lists the owner's channels it may send to or read from. |
zas_pair | Pairs this agent with a Zas account. The first call returns a URL for the owner to open; a later call says whether they approved. If the page shows a code, a call with code claims with it. In a profile that is already paired, approval replaces the old agent. |
zas_send_file | Sends a file from this machine into one of the owner's channels. Returns the item id, or a job id when the upload takes longer than a minute. |
zas_send_note | Sends a note — plain text, or a code snippet with its language — into one of the owner's channels. |
zas_send_direct | Sends a file through Directo: a live, device-to-device transfer into a channel in Directo mode. Nothing is stored. The owner presses Receive on another device within ten minutes; the call returns the result, or a job id after a minute. |
zas_send_direct_fallback | After a Directo send failed in flight, delivers the same file through reliable delivery: encrypted on this machine, stored in Cloudflare R2 for up to 24 hours, off the owner's quota. The owner's choice; the model is told to ask. |
zas_receive_direct | Receives a file the owner sends through Directo, onto this machine. Waits for the offer, takes it, and writes the file to disk. Needs a grant that includes reading, and a channel in Directo mode. Returns the path written, or a job id after a minute. |
zas_receive_direct_fallback | After a Directo receive failed in flight, downloads the encrypted copy the sender chose to store, and decrypts it to the same destination. |
zas_list_items | Lists the most recent items in one of the owner's channels. Needs a grant that includes reading. |
zas_get_item | Fetches one item. A note comes back as text; a file is written to disk. It never overwrites, so the path it answers with can differ from the one you asked for. |
zas_jobs | Lists the sends and Directo transfers this server started, newest first, with the phase each one reached — and where a job_id from a long send is redeemed. |
channel takes a channel name or a channel id. A name has to match exactly one
of the channels you granted; with exactly one grant, zas_send_file,
zas_send_note, zas_send_direct and zas_receive_direct can leave it out.
Directo needs a native module, node-datachannel,
WebRTC for Node. npm installs a prebuilt binary for Windows, macOS and Linux;
the module loads the first time a Directo tool runs, and a machine where it
cannot load answers webrtc_unavailable. Every other tool works without it.
Two things worth knowing before you point a model at your account:
zas_send_file and zas_send_direct send any file this process can read —
~/.ssh/id_rsa and a .env included. Confirm with the owner before sending secrets, keys or
credentials. Its tool description says so, so the model reads it too.zas_get_item and zas_receive_direct write a new file under dest, or
under the system temp directory when you leave dest out. Neither ever
overwrites an existing file: a name that is taken gets a suffix, and the path
they answer with is the one they actually wrote.Everything those tools touch lands inside your own account and your own machine. Revoking the agent stops all of them.
This package reports how it is used, so defects like a release that could not receive a file at all stop being invisible. It is on by default and it is yours to turn off:
ZAS_AGENT_TELEMETRY=0 and the cross-vendor DO_NOT_TRACK=1 do the same
without writing anything. The choice lives in ~/.zas/agent/settings.json and
covers every profile on the machine; pairing again does not reset it.
zas_status always says which way it is set.
What one report carries, and nothing else: the tool that ran, whether it worked, the closed error code when it did not, which of four duration buckets it fell in, and the package version. No file name, no file contents, no path, no channel name, no error message, no stack.
Where it goes: to Zas, never to an analytics service directly. This package holds no analytics token and opens no connection to a third party, so no address of yours reaches one. The report is attributed to the Zas account the agent is paired with, which the server reads from the session — the request carries no identifier of its own. Nothing is reported before pairing, because until then there is no account it could belong to.
A report is never allowed to matter: it is sent after the answer, waited on for at most five seconds, tried once, and dropped in silence if it fails.
One directory per profile, so one machine can hold a Claude Code agent and a Codex agent side by side without either reading the other's keys:
| OS | Path |
|---|---|
| macOS, Linux | ~/.zas/agent/<profile>/ |
| Windows | %USERPROFILE%\.zas\agent\<profile>\ |
Four files in the profile directory, all written through a temporary file and renamed into place, so a crash mid-write cannot leave half a file behind:
identity.json — the agent uid, the owner uid, the name, and the two key
pairs. Back it up like a private key, or delete it and pair again.pending.json — a pairing that has not been claimed yet. Removed on
completion, and on a pairing that expired or was cancelled.grants.json — a one-minute cache of GET /v1/agents/me: which channels,
and the sealed key for each. The channel name stays encrypted here.
Disposable.fingerprints.json — hashes of what an identical send produced in the last
ten minutes, so a retried tool call answers without touching the network. It
stores hashes, never a title or a note's first line. Disposable.One more file sits next to the profiles, in ~/.zas/agent/:
settings.json — whether this machine reports usage, and when the notice
above was last printed. It belongs to the machine, not to a profile, so one
decision covers every agent on it.On macOS and Linux the directory is created 0700 and every file 0600. On
Windows those bits have no effect: the files carry the permissions of the user
profile they live in, and the package does not try to set any others.
Deleting the directory makes this machine forget the agent. It does not revoke anything: the account side is closed from the web app, under Settings → Agents → Revoke.
A pairing is one profile's key pair, not the machine and not the program. A
profile holds one agent. Running zas-agent pair in a profile that is already
paired opens a replacement: the terminal signs the request with the old
identity, the approval page says which agent it replaces and fills in its name
and channels, and the old agent is revoked in the same step that creates the
new one. Its row stays under Settings → Agents as revoked until you remove it,
and it keeps costing a slot until then. If the old agent was already revoked
or removed, pair says so and creates a new agent.
One machine that runs Claude Code and Codex has two profiles (claude-code
and codex), two agents, two approvals and two rows. Revoking one does not
touch the other. Two programs pointed at the same --profile act as one
agent: same keys, same channels, one row.
| Setting | Default | What it changes |
|---|---|---|
--profile <name> | claude-code | Which identity directory this process uses. Letters, digits, ., _ and -, up to 64, and it may not start with a dot. |
ZAS_AGENT_HOME | ~/.zas/agent | Where the profile directories live. |
ZAS_AGENT_TELEMETRY | unset | 0 turns usage reporting off for this process, 1 on. It outranks the stored choice. |
DO_NOT_TRACK | unset | 1 turns usage reporting off. Read only to turn it off. |
ZAS_WEB_BASE | https://zas.red | The web app the pairing URL points at. |
ZAS_API_BASE | https://zas.red/api | The API. |
ZAS_TOKEN_BASE | https://zas.red/anon-token | The challenge and token routes. |
ZAS_OPRF_BASE | https://zas.red/oprf | The blind key-derivation service. |
ZAS_FIREBASE_PROJECT | zas-me | The project whose Firestore the read path queries. |
ZAS_FIREBASE_API_KEY | the public web key | The key used to exchange a custom token for a session. |
Only --profile, ZAS_AGENT_HOME and the two telemetry switches are worth
setting by hand. The rest exist
so the package can be pointed at a test deployment.
This repository is the public mirror of the agent/ package of Zas's private
monorepo. It is produced by scripts/export-public.mjs, which builds the
package with esbuild and uses the build's own metafile to decide what to copy:
src/, test/, the package files, and the nine src/shared/* modules the
agent imports. Those shared modules are the client-side crypto and format code — the
chunker, the manifest, the envelope, the key derivation — that the Zas web app
also ships, which is why the encryption the agent performs is the encryption the
app performs. The Zas server is not open source.
Pull requests are welcome against this repository. The maintainer applies accepted changes back into the monorepo, and the next export brings them here. See CONTRIBUTING.md.
Please report vulnerabilities privately. SECURITY.md says how, and what to expect.
MIT. See LICENSE.