@ziffer-io/client 0.1.0 → 0.1.1
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/README.md +62 -49
- package/package.json +14 -11
package/README.md
CHANGED
|
@@ -1,69 +1,82 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ziffer-io/client
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
|
|
3
|
+
The ZIFFER client for TypeScript. Propose an action, wait for the decision, and verify the signed
|
|
4
|
+
receipt in your own process before you act.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
5
7
|
|
|
6
8
|
```bash
|
|
7
9
|
npm install @ziffer-io/client
|
|
8
10
|
```
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
with it. `verifyReceipt` is **re-exported unchanged** from the verifier's own package, so there is
|
|
12
|
-
one verifier in your dependency tree rather than two that can disagree — the tests assert it is
|
|
13
|
-
the same function object, so the re-export cannot decay into a copy quietly.
|
|
12
|
+
Node 22 or later. The wire types and the receipt verifier are installed with it.
|
|
14
13
|
|
|
15
|
-
##
|
|
14
|
+
## Quickstart
|
|
16
15
|
|
|
17
16
|
```ts
|
|
18
|
-
import {
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
import { ZifferClient, verifyReceipt, type TrustAnchor } from '@ziffer-io/client';
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
const anchor: TrustAnchor =
|
|
20
|
+
const keys = JSON.parse(readFileSync(process.env.ZIFFER_TRUST_ANCHOR, 'utf8'));
|
|
21
|
+
const anchor: TrustAnchor = {
|
|
22
|
+
classical: Buffer.from(keys.ed25519_pk_hex, 'hex'),
|
|
23
|
+
pq: Buffer.from(keys.mldsa65_pk_hex, 'hex'),
|
|
24
|
+
minSuite: process.env.ZIFFER_SUITE_FLOOR,
|
|
25
|
+
};
|
|
22
26
|
|
|
27
|
+
const client = new ZifferClient(process.env.ZIFFER_API_URL, process.env.ZIFFER_API_KEY);
|
|
23
28
|
const submitted = await client.propose(proposal);
|
|
24
29
|
const decision = await client.wait(submitted.decision_id, { timeoutMs: 30_000 });
|
|
25
30
|
if (decision.outcome !== 'ALLOW') throw new Error(`ziffer refused: ${decision.clause}`);
|
|
26
31
|
|
|
27
32
|
verifyReceipt(decision.receipt, new TextEncoder().encode(JSON.stringify(proposal)), anchor);
|
|
28
|
-
await bank.transfer(amount, toAccount);
|
|
33
|
+
await bank.transfer(amount, toAccount); // your line, unchanged
|
|
29
34
|
```
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
36
|
+
You pass the proposal twice on purpose. The verifier hashes the bytes you hand it and compares
|
|
37
|
+
them with the receipt's claim. The check is against your copy, not against ours. Key order and
|
|
38
|
+
spacing do not matter.
|
|
39
|
+
|
|
40
|
+
`verifyReceipt` checks the answer. Your own `if` is what stops the action.
|
|
41
|
+
|
|
42
|
+
An action that needs human approval answers `ATTEST` and no receipt. Keep polling
|
|
43
|
+
`client.wait` for the same decision id until `decision.receipt` is present, then verify it.
|
|
44
|
+
|
|
45
|
+
Under TypeScript's strict settings `process.env` values are typed `string | undefined`. Read them
|
|
46
|
+
through a small helper that throws when one is not set. The SDK guide has one.
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
| Variable | What it is | Where the value comes from |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `ZIFFER_API_KEY` | Your API key. It carries your tenant, so no request names a tenant. | We issue it. It expires after 90 days unless you ask for another lifetime. |
|
|
53
|
+
| `ZIFFER_TRUST_ANCHOR` | Path to the public key file your receipts are signed under. | We give you the file. Take it from us, never from the API you are checking. |
|
|
54
|
+
| `ZIFFER_SUITE_FLOOR` | The weakest signature suite you will accept. | You choose it. There is no default. |
|
|
55
|
+
| `ZIFFER_API_URL` | The base URL of the ZIFFER deployment you call. | We give it to you with your key. |
|
|
56
|
+
|
|
57
|
+
## When a request is refused
|
|
58
|
+
|
|
59
|
+
Every refusal is a thrown `Refusal` whose `clause` names the rule that fired; the table of every
|
|
60
|
+
clause, what it means and what to do is at https://ziffer.io/docs/refusals. Narrow with
|
|
61
|
+
`instanceof Refusal`, record the clause, and do not retry it.
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
- Quickstart: https://ziffer.io/docs/quickstart
|
|
66
|
+
- Integrating the SDK: https://ziffer.io/docs/developers/sdk
|
|
67
|
+
- Sandbox tenants: https://ziffer.io/docs/developers/sandbox
|
|
68
|
+
- Every refusal: https://ziffer.io/docs/refusals
|
|
69
|
+
- Policy by example: https://ziffer.io/docs/policy/by-example
|
|
70
|
+
- Glossary: https://ziffer.io/docs/glossary
|
|
71
|
+
|
|
72
|
+
## Support
|
|
73
|
+
|
|
74
|
+
Write to hello@ziffer.io. Your API key, your trust anchor file and your suite floor come from us.
|
|
75
|
+
So does an answer about a refusal you cannot explain.
|
|
62
76
|
|
|
63
|
-
##
|
|
77
|
+
## License
|
|
64
78
|
|
|
65
|
-
Proprietary. Copyright (c) 2026 code75 SASU, Paris, France. ZIFFER is a registered trademark of
|
|
66
|
-
SASU. This package is
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
their own licences.
|
|
79
|
+
Proprietary. Copyright (c) 2026 code75 SASU, Paris, France. ZIFFER is a registered trademark of
|
|
80
|
+
code75 SASU. This package is not open source. Its use is governed by your agreement with code75
|
|
81
|
+
and by `LICENSE` beside this file. The open-source components it redistributes are listed in
|
|
82
|
+
`THIRD-PARTY-NOTICES`, under their own licences.
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziffer-io/client",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "ZIFFER TypeScript client: propose an action, wait for the decision, verify the receipt before you act",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ziffer",
|
|
7
|
+
"agent",
|
|
8
|
+
"ai-agent",
|
|
9
|
+
"guardrail",
|
|
10
|
+
"approval",
|
|
11
|
+
"receipt"
|
|
12
|
+
],
|
|
5
13
|
"author": "code75 SASU",
|
|
6
14
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
-
"comment-license": "The SDK is proprietary (code75 SASU). \"license\" is the SPDX escape hatch for exactly this case: there is no SPDX identifier for these terms, so the field points at the file that states them, and LICENSE ships in the tarball beside THIRD-PARTY-NOTICES. Do not put an OSI identifier here -- Apache-2.0 stood in these four files until ACP-214 and was wrong the whole time.",
|
|
8
15
|
"type": "module",
|
|
9
16
|
"main": "./dist/index.js",
|
|
10
17
|
"types": "./dist/index.d.ts",
|
|
@@ -32,16 +39,12 @@
|
|
|
32
39
|
"publishConfig": {
|
|
33
40
|
"access": "public"
|
|
34
41
|
},
|
|
35
|
-
"comment-no-provenance": "There is deliberately no \"provenance\": true here, and it was removed rather than never added. npm generates a provenance attestation only from a recognised CI runner, and its documentation states plainly that provenance is NOT SUPPORTED for private repositories (docs.npmjs.com/trusted-publishers, read 2026-09-03) -- ziffer-hq/ziffer is private (`gh api` says so). Setting the flag does not degrade to a warning: npm attempts the attestation and the publish FAILS, so the field would have broken the operator's very first publish from a laptop and every CI publish after it. tools/release-npm.sh asserts the field stays absent, and that assertion is the thing to delete on the day this repository becomes public -- at which point trusted publishing generates provenance on its own, with no flag at all.",
|
|
36
42
|
"ziffer": {
|
|
37
|
-
"enginePin": "
|
|
43
|
+
"enginePin": "8ae262196dbe75210a975a456785df5d9f0524a3"
|
|
38
44
|
},
|
|
39
|
-
"comment-enginePin": "The engine commit whose wire types this client speaks and whose verifier it re-exports. A COPY of the rev in Cargo.toml, which is the one authority tools/guard.sh reads; tools/release-npm.sh refuses to release when the two differ, by name. tools/bump-pin.sh does not move this field -- see packages/types/package.json for why and what closing it costs.",
|
|
40
|
-
"comment-deps": "Exactly two: @ziffer-io/types because the wire format is the one permitted shared definition (it IS the schemas, generated), and @ziffer-io/verify because verification has ONE home and this client re-exports it rather than growing a second (ACP-197 runbook section 6). No HTTP dependency: Node's own fetch carries two routes; a framework here is surface without a claim.",
|
|
41
|
-
"comment-types-was-a-git-dependency": "Until ACP-214 the first of those two was `git+https://ziffer-hq@github.com/ziffer-hq/agent-control-plane.git#<pin>&path:/packages/acp-types`. That is a private repository, so the published client would have been uninstallable rather than merely awkward; and pnpm runs `prepare` for a git dependency, so a consumer who could reach it would have compiled our wire types on their own machine. It is now a workspace dependency on packages/types, which `pnpm pack` rewrites to that package's exact published version -- measured, and asserted again by tools/release-npm.sh over every tarball.",
|
|
42
45
|
"dependencies": {
|
|
43
|
-
"@ziffer-io/
|
|
44
|
-
"@ziffer-io/
|
|
46
|
+
"@ziffer-io/types": "0.1.1",
|
|
47
|
+
"@ziffer-io/verify": "0.1.1"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
50
|
"@types/node": "^22.15.0",
|
|
@@ -50,7 +53,7 @@
|
|
|
50
53
|
"scripts": {
|
|
51
54
|
"build": "tsc -b",
|
|
52
55
|
"typecheck": "tsc -b",
|
|
53
|
-
"pretest": "tsc -b && if ! find dist -name '*.test.js' -print -quit | grep -q .; then echo '
|
|
56
|
+
"pretest": "tsc -b && if ! find dist -name '*.test.js' -print -quit | grep -q .; then echo 'no compiled test file under dist/ -- node --test reports 0 tests and exits 0, so this package would go green having run nothing' >&2; exit 1; fi",
|
|
54
57
|
"test": "tsc -b && node --test \"dist/**/*.test.js\""
|
|
55
58
|
}
|
|
56
59
|
}
|