zas-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-09-02
11
+
12
+ ### Added
13
+
14
+ - First release.
15
+ - Pairing: the agent mints its own key pair on the machine and proves it holds
16
+ the private half by signing a P-256 challenge; the owner approves it from the
17
+ web app and compares a fingerprint before they do.
18
+ - `zas_send_file` and `zas_send_note`: send a file or a note into a granted
19
+ channel, end-to-end encrypted on this machine.
20
+ - `zas_list_items` and `zas_get_item`: list a channel's recent items and fetch
21
+ one, where the grant includes reading.
22
+ - Per-channel grants: the agent holds one channel key per granted channel and
23
+ no key for any other, and the owner can add or drop a channel at any time.
24
+ - `--profile`, so one machine can run a Claude Code agent and a Codex agent as
25
+ two separate identities that cannot read each other's keys.
26
+ - Install snippets for Claude Code and Codex, printed by `zas-agent pair`.
27
+
28
+ [Unreleased]: https://github.com/soke1556/zas-agent/compare/v0.1.0...HEAD
29
+ [0.1.0]: https://github.com/soke1556/zas-agent/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sebastian Fernandez Quezada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # zas-agent
2
+
3
+ An MCP server that lets a coding agent — Claude Code, Codex, or anything that
4
+ speaks MCP — send files and notes into your [Zas](https://zas.red) channels, and
5
+ read items back out of them.
6
+
7
+ [![npm](https://img.shields.io/npm/v/zas-agent)](https://www.npmjs.com/package/zas-agent)
8
+ [![CI](https://github.com/soke1556/zas-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/soke1556/zas-agent/actions/workflows/ci.yml)
9
+ [![CodeQL](https://github.com/soke1556/zas-agent/actions/workflows/codeql.yml/badge.svg)](https://github.com/soke1556/zas-agent/actions/workflows/codeql.yml)
10
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/soke1556/zas-agent/badge)](https://scorecard.dev/viewer/?uri=github.com/soke1556/zas-agent)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
12
+
13
+ ## What it does
14
+
15
+ Your coding agent gets seven tools. It can send a file or a note into a channel
16
+ you picked, list what is in that channel, and pull one item back onto disk.
17
+ Everything it sends is encrypted on your machine before it leaves, lands in your
18
+ account, and is marked in the channel as sent by that agent. The agent has an
19
+ identity of its own and never holds your account key: pairing mints a key pair
20
+ here, you approve it from the web app, and from then on it signs a challenge to
21
+ get a short-lived session.
22
+
23
+ ## Install
24
+
25
+ Pair the machine first, then hand the command to your harness.
26
+
27
+ **Pair**
28
+
29
+ ```
30
+ npx -y zas-agent pair --profile claude-code
31
+ ```
32
+
33
+ **Claude Code**
34
+
35
+ ```
36
+ claude mcp add zas -- npx -y zas-agent --profile claude-code
37
+ ```
38
+
39
+ **Codex**
40
+
41
+ ```
42
+ codex mcp add zas -- npx -y zas-agent --profile codex
43
+ ```
44
+
45
+ Codex by hand, if you would rather edit `~/.codex/config.toml`:
46
+
47
+ ```toml
48
+ [mcp_servers.zas]
49
+ command = "npx"
50
+ args = ["-y", "zas-agent", "--profile", "codex"]
51
+ ```
52
+
53
+ ### What pairing prints
54
+
55
+ `zas-agent pair` mints the key pair, registers the public halves, and waits. The
56
+ terminal shows four things:
57
+
58
+ ```
59
+ Abrí esta página con tu cuenta de Zas:
60
+ https://zas.red/agents/pair?p=...
61
+ Código: ABCD-EFGH
62
+ Huella: 1a2b 3c4d 5e6f 7a8b
63
+ Esperando la aprobación… (vence en 10 minutos)
64
+ ```
65
+
66
+ Open the link signed in to your Zas account. Check that the fingerprint on the
67
+ page is the one in your terminal, name the agent, and tick the channels it may
68
+ use — sending is the default, reading is a separate switch. The code is eight
69
+ characters in two groups, and it is what you compare before you approve. The
70
+ pairing is good for ten minutes; past that, run the command again.
71
+
72
+ You can also start the flow from the coding agent with the `zas_pair` tool: the
73
+ first call hands back the URL and the code, and a later call says whether the
74
+ approval landed.
75
+
76
+ ## Why you can trust it
77
+
78
+ Every line here is a fact. The ones about this package you can check in this
79
+ repository. Where a property is enforced by the Zas server, which is not open
80
+ source, the sentence says so.
81
+
82
+ - **The agent has an identity of its own.** `zas-agent pair` generates two key
83
+ pairs on your machine — X25519 to receive channel keys, P-256 to sign
84
+ sign-ins — and the private halves never leave it. The agent never holds your
85
+ account key, and the key-derivation service refuses account-key derivation to
86
+ an agent *(server-side)*.
87
+ - **You approve it, and you choose the channels.** Pairing never
88
+ auto-approves. The approval page shows the harness, the host, the key
89
+ fingerprint and the code, and you compare the code with your terminal before
90
+ anything is granted.
91
+ - **It holds one key per granted channel, and no key for any other.** Each
92
+ grant carries that channel's key sealed to the agent's X25519 public key. A
93
+ channel you did not grant has no key here to decrypt with, and the server
94
+ checks the live grant on every request *(server-side)*.
95
+ - **No password and no API key.** Signing in is a signed challenge traded for a
96
+ one-hour token. There is no password, no API key and no refresh token on
97
+ disk: the agent re-signs from its P-256 key when the token ages out.
98
+ - **Everything it sends is visible as its work.** Every item carries the `>_`
99
+ agent mark and the agent's name, in the channel, on every device you read Zas
100
+ from.
101
+ - **You can revoke it at any time.** Settings → Agentes → the agent → Revocar.
102
+ The session stops, its refresh tokens are revoked *(server-side)*, and the
103
+ next tool call answers "the owner revoked this agent". What it already sent
104
+ stays where it is. You can also drop a single channel and keep the rest.
105
+ - **Content is end-to-end encrypted, the same way the app does it.** The
106
+ chunking, the manifest and the envelope formats under `src/shared/` are the
107
+ same modules the Zas web app ships. The server stores ciphertext and never
108
+ sees a channel key, a channel name or item plaintext *(server-side)*.
109
+ - **The source is here, and the releases are built from it.** Every npm release
110
+ is published by the `release.yml` workflow in this repository, with npm
111
+ provenance, so the tarball on npm can be traced back to a commit and a
112
+ workflow run.
113
+
114
+ ## What it cannot do
115
+
116
+ The package refuses some of these on its own, before a request is made. The
117
+ ones marked *(server-side)* are enforced by the Zas server.
118
+
119
+ - **No channel you did not grant.** Not by name, not by id.
120
+ - **No shared channel you merely joined, and no workspace channel.** Grants
121
+ exist only on channels your account owns and that no organization manages
122
+ *(server-side)*.
123
+ - **No reading unless the grant says so.** `read` is a separate switch from
124
+ `send`; without it, `zas_list_items` and `zas_get_item` are refused.
125
+ - **No sending into a view-only channel, and none into a channel in Directo
126
+ mode.** Both are refused before a byte is uploaded.
127
+ - **Nothing outside its allowlist.** The API refuses an agent on every route
128
+ that is not on a short, explicit list, and Firestore rules refuse it your
129
+ account document, your devices, and any channel without an active read grant
130
+ *(server-side)*.
131
+ - **Rate limited by the server**, on its own buckets, with the key-derivation
132
+ budget charged to your account so ten agents are not ten times your own
133
+ allowance *(server-side)*.
134
+ - **Ten agents per account** *(server-side)*.
135
+ - **Files up to 5 GiB**, and in practice less: the agent reads a file into
136
+ memory to hash it, so the machine's memory is the real ceiling.
137
+
138
+ ### The error vocabulary
139
+
140
+ The agent answers in a closed set of codes. Anything a server route says that
141
+ is not in this set collapses to `upload_failed` or `network`, so no raw server
142
+ string ever reaches a terminal.
143
+
144
+ | Code | What it means |
145
+ | --- | --- |
146
+ | `not_paired` | This machine is not paired yet. |
147
+ | `identity_corrupt` | The identity file on disk is damaged. |
148
+ | `agent_revoked` | The owner revoked this agent. |
149
+ | `agent_forbidden` | Only the account owner can do that. |
150
+ | `grant_missing` | This agent has no access to that channel. |
151
+ | `send_forbidden` | This agent cannot send to that channel. |
152
+ | `read_forbidden` | This agent cannot read that channel. |
153
+ | `direct_mode` | That channel is in Directo mode. |
154
+ | `not_direct_mode` | That channel is not in Directo mode. |
155
+ | `key_stale` | The channel key changed; the owner refreshes it by opening Zas. |
156
+ | `quota_exceeded` | The account reached its storage limit. |
157
+ | `rate_limited` | Too many sends in a row. |
158
+ | `file_too_big` | The file is over the plan limit. |
159
+ | `duplicate` | That item is already in the channel. |
160
+ | `not_found` | That item is not in the channel. |
161
+ | `invalid_cap` | That file is no longer available. |
162
+ | `write_failed` | The download destination could not be written. |
163
+ | `pairing_expired` | The pairing expired; pair again. |
164
+ | `pairing_cancelled` | The owner cancelled the pairing. |
165
+ | `feature_disabled` | Agents are not enabled for this account yet. |
166
+ | `upload_failed` | The upload failed. |
167
+ | `oprf_failed` | Zas did not answer correctly while preparing the file. |
168
+ | `network` | Zas cannot be reached. |
169
+ | `sign_in_failed` | Zas did not accept this agent session. |
170
+ | `bad_signature` | Zas rejected this agent's signature; pair it again. |
171
+ | `missing_token` | The session token is missing; pair the agent again. |
172
+ | `internal` | Something failed inside the agent. |
173
+
174
+ Every code comes back as one sentence in Spanish and one in English, never as a
175
+ stack trace.
176
+
177
+ ## Tools
178
+
179
+ | Tool | What it does |
180
+ | --- | --- |
181
+ | `zas_status` | Says whether this machine is paired with a Zas account, and lists the owner's channels this agent may send to or read from. |
182
+ | `zas_pair` | Pairs this machine with a Zas account. The first call returns a URL and a code for the owner to approve; a later call says whether they did. |
183
+ | `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. |
184
+ | `zas_send_note` | Sends a note — plain text, or a code snippet with its language — into one of the owner's channels. |
185
+ | `zas_list_items` | Lists the most recent items in one of the owner's channels. Needs a grant that includes reading. |
186
+ | `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. |
187
+ | `zas_jobs` | Lists the sends this server started, newest first, with the phase each one reached — and where a `job_id` from a long send is redeemed. |
188
+
189
+ `channel` takes a channel name or a channel id. A name has to match exactly one
190
+ of the channels you granted; with exactly one grant, `zas_send_file` and
191
+ `zas_send_note` can leave it out.
192
+
193
+ Two things worth knowing before you point a model at your account:
194
+
195
+ - `zas_send_file` sends any file this process can read — `~/.ssh/id_rsa` and a
196
+ `.env` included. Confirm with the owner before sending secrets, keys or
197
+ credentials. Its tool description says so, so the model reads it too.
198
+ - `zas_get_item` writes a new file under `dest`, or under the system temp
199
+ directory when you leave `dest` out. It never overwrites an existing file: a
200
+ name that is taken gets a suffix, and the path it answers with is the one it
201
+ actually wrote.
202
+
203
+ Everything either tool touches lands inside your own account and your own
204
+ machine. Revoking the agent stops both.
205
+
206
+ ## Data on disk
207
+
208
+ One directory per profile, so one machine can hold a Claude Code agent and a
209
+ Codex agent side by side without either reading the other's keys:
210
+
211
+ | OS | Path |
212
+ | --- | --- |
213
+ | macOS, Linux | `~/.zas/agent/<profile>/` |
214
+ | Windows | `%USERPROFILE%\.zas\agent\<profile>\` |
215
+
216
+ Four files, all written through a temporary file and renamed into place, so a
217
+ crash mid-write cannot leave half a file behind:
218
+
219
+ - `identity.json` — the agent uid, the owner uid, the name, and the two key
220
+ pairs. Back it up like a private key, or delete it and pair again.
221
+ - `pending.json` — a pairing that has not been approved yet. Removed on
222
+ approval, and on a pairing that expired or was cancelled.
223
+ - `grants.json` — a one-minute cache of `GET /v1/agents/me`: which channels,
224
+ and the sealed key for each. The channel name stays encrypted here.
225
+ Disposable.
226
+ - `fingerprints.json` — hashes of what an identical send produced in the last
227
+ ten minutes, so a retried tool call answers without touching the network. It
228
+ stores hashes, never a title or a note's first line. Disposable.
229
+
230
+ On macOS and Linux the directory is created `0700` and every file `0600`. On
231
+ Windows those bits have no effect: the files carry the permissions of the user
232
+ profile they live in, and the package does not try to set any others.
233
+
234
+ Deleting the directory makes this machine forget the agent. It does not revoke
235
+ anything: the account side is closed from the web app, under
236
+ Settings → Agentes → Revocar.
237
+
238
+ ## Configuration
239
+
240
+ | Setting | Default | What it changes |
241
+ | --- | --- | --- |
242
+ | `--profile <name>` | `claude-code` | Which identity directory this process uses. Letters, digits, `.`, `_` and `-`, up to 64, and it may not start with a dot. |
243
+ | `ZAS_AGENT_HOME` | `~/.zas/agent` | Where the profile directories live. |
244
+ | `ZAS_WEB_BASE` | `https://zas.red` | The web app the pairing URL points at. |
245
+ | `ZAS_API_BASE` | `https://zas.red/api` | The API. |
246
+ | `ZAS_TOKEN_BASE` | `https://zas.red/anon-token` | The challenge and token routes. |
247
+ | `ZAS_OPRF_BASE` | `https://zas.red/oprf` | The blind key-derivation service. |
248
+ | `ZAS_FIREBASE_PROJECT` | `zas-me` | The project whose Firestore the read path queries. |
249
+ | `ZAS_FIREBASE_API_KEY` | the public web key | The key used to exchange a custom token for a session. |
250
+
251
+ Only `--profile` and `ZAS_AGENT_HOME` are worth setting by hand. The rest exist
252
+ so the package can be pointed at a test deployment.
253
+
254
+ ## Development
255
+
256
+ ```
257
+ git clone https://github.com/soke1556/zas-agent
258
+ cd zas-agent
259
+ npm ci
260
+ npm test
261
+ npm run typecheck
262
+ npm run build
263
+ node dist/cli.js --version
264
+ ```
265
+
266
+ This repository is the public mirror of the `agent/` package of Zas's private
267
+ monorepo. It is produced by `scripts/export-public.mjs`, which builds the
268
+ package with esbuild and uses the build's own metafile to decide what to copy:
269
+ `src/`, `test/`, the package files, and the nine `src/shared/*` modules the
270
+ agent imports. Those shared modules are the client-side crypto and format code — the
271
+ chunker, the manifest, the envelope, the key derivation — that the Zas web app
272
+ also ships, which is why the encryption the agent performs is the encryption the
273
+ app performs. The Zas server is not open source.
274
+
275
+ Pull requests are welcome against this repository. The maintainer applies
276
+ accepted changes back into the monorepo, and the next export brings them here.
277
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
278
+
279
+ ## Security
280
+
281
+ Please report vulnerabilities privately. [SECURITY.md](SECURITY.md) says how,
282
+ and what to expect.
283
+
284
+ ## License
285
+
286
+ MIT. See [LICENSE](LICENSE).