@agentmesh-p2p/sdk 0.0.0-stage → 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.
Files changed (68) hide show
  1. package/README.md +182 -2
  2. package/dist/auth.d.ts +66 -0
  3. package/dist/auth.js +164 -0
  4. package/dist/authorizer.d.ts +41 -0
  5. package/dist/authorizer.js +201 -0
  6. package/dist/base58.d.ts +2 -0
  7. package/dist/base58.js +57 -0
  8. package/dist/biscuit.d.ts +75 -0
  9. package/dist/biscuit.js +250 -0
  10. package/dist/bytes.d.ts +10 -0
  11. package/dist/bytes.js +51 -0
  12. package/dist/challenges.d.ts +16 -0
  13. package/dist/challenges.js +55 -0
  14. package/dist/conformance-join.d.ts +1 -0
  15. package/dist/conformance-join.js +257 -0
  16. package/dist/conformance.d.ts +1 -0
  17. package/dist/conformance.js +60 -0
  18. package/dist/controlplane.d.ts +123 -0
  19. package/dist/controlplane.js +368 -0
  20. package/dist/credential.d.ts +66 -0
  21. package/dist/credential.js +124 -0
  22. package/dist/discovery.d.ts +50 -0
  23. package/dist/discovery.js +150 -0
  24. package/dist/gen/agentmesh_pb.d.ts +2740 -0
  25. package/dist/gen/agentmesh_pb.js +591 -0
  26. package/dist/gen/datalog.d.ts +39 -0
  27. package/dist/gen/datalog.js +84 -0
  28. package/dist/host.d.ts +47 -0
  29. package/dist/host.js +109 -0
  30. package/dist/http1.d.ts +68 -0
  31. package/dist/http1.js +371 -0
  32. package/dist/identity.d.ts +31 -0
  33. package/dist/identity.js +125 -0
  34. package/dist/index.d.ts +18 -0
  35. package/dist/index.js +30 -0
  36. package/dist/libp2p-http-node.d.ts +17 -0
  37. package/dist/libp2p-http-node.js +116 -0
  38. package/dist/libp2p-http.d.ts +165 -0
  39. package/dist/libp2p-http.js +581 -0
  40. package/dist/mcp.d.ts +67 -0
  41. package/dist/mcp.js +157 -0
  42. package/dist/mesh.d.ts +151 -0
  43. package/dist/mesh.js +370 -0
  44. package/dist/platform/ingress.browser.d.ts +1 -0
  45. package/dist/platform/ingress.browser.js +16 -0
  46. package/dist/platform/ingress.d.ts +1 -0
  47. package/dist/platform/ingress.js +15 -0
  48. package/dist/platform/state.browser.d.ts +5 -0
  49. package/dist/platform/state.browser.js +47 -0
  50. package/dist/platform/state.d.ts +5 -0
  51. package/dist/platform/state.js +44 -0
  52. package/dist/platform/transports.browser.d.ts +3 -0
  53. package/dist/platform/transports.browser.js +27 -0
  54. package/dist/platform/transports.d.ts +3 -0
  55. package/dist/platform/transports.js +27 -0
  56. package/dist/platform/types.d.ts +7 -0
  57. package/dist/platform/types.js +14 -0
  58. package/dist/platform/wasm.browser.d.ts +2 -0
  59. package/dist/platform/wasm.browser.js +16 -0
  60. package/dist/platform/wasm.d.ts +2 -0
  61. package/dist/platform/wasm.js +56 -0
  62. package/dist/session.d.ts +316 -0
  63. package/dist/session.js +1269 -0
  64. package/dist/sync.d.ts +41 -0
  65. package/dist/sync.js +111 -0
  66. package/dist/tar.d.ts +25 -0
  67. package/dist/tar.js +406 -0
  68. package/package.json +74 -4
@@ -0,0 +1,257 @@
1
+ // Copyright 2026 Google LLC
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // Mesh member driven by tests/integration/sdk_mesh_test.go: enrolls, joins
15
+ // the mesh through a real router, prints one JSON line describing the
16
+ // session, then takes JSON commands on stdin, one per line, until stdin
17
+ // closes:
18
+ //
19
+ // {"cmd": "auth", "addr": "<multiaddr>"} connect (through a relay when the
20
+ // address says /p2p-circuit) and
21
+ // run the auth handshake
22
+ // {"cmd": "discover", "type": "mcp", "name": "calc"}
23
+ // DHT lookup for a service's providers
24
+ // {"cmd": "tools", "addr": "<multiaddr>", "service": "mcp://calc",
25
+ // "required_labels": {"region": "eu"}} list a provider's tools; the labels,
26
+ // if given, must be on its credential
27
+ // {"cmd": "call", "addr": "<multiaddr>", "service": "mcp://calc",
28
+ // "tool": "add", "args": {...}, "required_labels": {...}}
29
+ // call one tool
30
+ // {"cmd": "accept", "name": "agent", "target": "<url>"}
31
+ // accept A2A requests for this
32
+ // member's agent; without target,
33
+ // a handler in this process answers
34
+ // {"cmd": "http", "addr": "<multiaddr>", "service": "a2a://agent",
35
+ // "path": "/card", "method": "GET", "body": "..."}
36
+ // call an HTTP service over the mesh
37
+ // {"cmd": "peers"} peers that authenticated to us
38
+ // {"cmd": "sync"} pull keys, bans and routers from
39
+ // the control plane now
40
+ // {"cmd": "banned"} peers banned by the control plane
41
+ // {"cmd": "quit"} leave the mesh and exit
42
+ //
43
+ // Each command gets one JSON line back. Same environment as conformance.ts;
44
+ // the Python SDK ships the same runner (python -m agent_mesh.conformance_join).
45
+ import { createInterface } from "node:readline";
46
+ import { DEFAULT_A2A_NAME } from "./libp2p-http.js";
47
+ import { AgentMesh } from "./mesh.js";
48
+ function requireEnv(name) {
49
+ const v = process.env[name];
50
+ if (!v) {
51
+ throw new Error(`${name} is required`);
52
+ }
53
+ return v;
54
+ }
55
+ function emit(obj) {
56
+ process.stdout.write(JSON.stringify(obj) + "\n");
57
+ }
58
+ /**
59
+ * Labels from an environment variable written "k=v,k2=v2"; a pair without
60
+ * "=" is refused (parseRequiredLabels), so a typo never switches a floor off.
61
+ */
62
+ function labelsFromEnv(name) {
63
+ const out = {};
64
+ for (const pair of (process.env[name] ?? "").split(",")) {
65
+ if (pair.trim() === "") {
66
+ continue;
67
+ }
68
+ const eq = pair.indexOf("=");
69
+ if (eq === -1) {
70
+ throw new Error(`${name}: invalid label ${JSON.stringify(pair.trim())}: expected key=value`);
71
+ }
72
+ out[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
73
+ }
74
+ return out;
75
+ }
76
+ function failure(cmd, err) {
77
+ return { cmd, ok: false, error: err instanceof Error ? `${err.name}: ${err.message}` : String(err) };
78
+ }
79
+ function requiredLabelsOf(command) {
80
+ return command.required_labels !== undefined ? { requiredLabels: command.required_labels } : {};
81
+ }
82
+ async function handle(session, command) {
83
+ try {
84
+ switch (command.cmd) {
85
+ case "auth": {
86
+ const timeoutMs = command.timeout_ms !== undefined ? Number(command.timeout_ms) : 15_000;
87
+ const verified = await session.authenticate(command.addr ?? "", AbortSignal.timeout(timeoutMs));
88
+ return {
89
+ cmd: "auth",
90
+ ok: true,
91
+ peer_id: verified.peerId,
92
+ roles: verified.roles,
93
+ labels: verified.labels,
94
+ expiration: Math.floor(verified.expiration.getTime() / 1000),
95
+ };
96
+ }
97
+ case "discover": {
98
+ const providers = await session.discover((command.type ?? "mcp"), command.name);
99
+ return { cmd: "discover", ok: true, providers: providers.map((p) => ({ peer_id: p.peerId, addrs: p.addrs })) };
100
+ }
101
+ case "tools": {
102
+ const tools = await session.listTools(command.addr ?? "", command.service ?? "", { signal: AbortSignal.timeout(15_000), ...requiredLabelsOf(command) });
103
+ return { cmd: "tools", ok: true, tools: tools.map((t) => t.name) };
104
+ }
105
+ case "call": {
106
+ const result = await session.callTool(command.addr ?? "", command.service ?? "", command.tool ?? "", command.args ?? {}, {
107
+ signal: AbortSignal.timeout(15_000),
108
+ ...requiredLabelsOf(command),
109
+ });
110
+ return { cmd: "call", ok: true, is_error: result.isError, text: result.text };
111
+ }
112
+ case "peers":
113
+ return { cmd: "peers", authenticated_peers: [...session.authenticatedPeers.keys()].sort() };
114
+ case "routers":
115
+ return {
116
+ cmd: "routers",
117
+ ok: true,
118
+ routers: session.routers.map((r) => r.peerId),
119
+ relay_addresses: session.relayAddresses.map((ma) => ma.toString()),
120
+ go_away_received: Object.fromEntries(session.goAwayReceived),
121
+ };
122
+ case "sync": {
123
+ const result = await session.sync();
124
+ return {
125
+ cmd: "sync",
126
+ ok: result.errors.length === 0,
127
+ error: result.errors.join("; "),
128
+ keys_changed: result.keysChanged,
129
+ refreshed: result.refreshed,
130
+ trusted_keys: session.mesh.credential.controlPlaneKeys.map((k) => Buffer.from(k).toString("hex")),
131
+ biscuit: Buffer.from(session.mesh.credential.biscuit).toString("base64"),
132
+ banned: session.banned.peers(),
133
+ router_addresses: session.mesh.credential.routerAddresses,
134
+ };
135
+ }
136
+ case "banned":
137
+ return { cmd: "banned", ok: true, banned: session.banned.peers(), authenticated_peers: [...session.authenticatedPeers.keys()].sort() };
138
+ case "accept": {
139
+ const spec = command.target !== undefined
140
+ ? { url: command.target }
141
+ : {
142
+ handler: (request, caller) => new Response(JSON.stringify({ sdk: "js", peer: caller.peerId, method: request.method, path: new URL(request.url).pathname }), {
143
+ headers: { "content-type": "application/json" },
144
+ }),
145
+ };
146
+ spec.name = command.name ?? DEFAULT_A2A_NAME;
147
+ const service = await session.acceptA2A(spec);
148
+ return { cmd: "accept", ok: true, service };
149
+ }
150
+ case "http": {
151
+ const res = await session.request(command.addr ?? "", command.service ?? "", command.path ?? "/", {
152
+ method: command.method ?? "GET",
153
+ ...(command.headers !== undefined ? { headers: command.headers } : {}),
154
+ ...(command.body !== undefined ? { body: command.body } : {}),
155
+ signal: AbortSignal.timeout(15_000),
156
+ });
157
+ return { cmd: "http", ok: true, status: res.status, headers: res.headers, body: res.text() };
158
+ }
159
+ default:
160
+ return failure(command.cmd, new Error(`unknown command ${JSON.stringify(command.cmd)}`));
161
+ }
162
+ }
163
+ catch (err) {
164
+ return failure(command.cmd, err);
165
+ }
166
+ }
167
+ async function main() {
168
+ const controlPlaneUrl = requireEnv("AGENTMESH_CONTROL_PLANE_URL");
169
+ // Enrollment is with a bootstrap token or a platform JWT, as the examples
170
+ // offer; a JWT file is read again at every refresh.
171
+ const bootstrapTokenPath = process.env.AGENTMESH_BOOTSTRAP_TOKEN_PATH;
172
+ const jwtPath = process.env.AGENTMESH_JWT_PATH;
173
+ if ((bootstrapTokenPath === undefined) === (jwtPath === undefined)) {
174
+ throw new Error("exactly one of AGENTMESH_BOOTSTRAP_TOKEN_PATH or AGENTMESH_JWT_PATH is required");
175
+ }
176
+ const stateDir = requireEnv("AGENTMESH_SDK_STATE_DIR");
177
+ const allowInsecure = process.env.AGENTMESH_INSECURE_CONTROL_PLANE === "1";
178
+ const listenAddrs = (process.env.AGENTMESH_SDK_LISTEN_ADDRS ?? "").split(",").filter((a) => a !== "");
179
+ // Labels this member declares at enrollment; the policy's allowed_labels
180
+ // decide whether the control plane attests them. AGENTMESH_SDK_EGRESS_REQUIRE_LABELS
181
+ // is the floor every provider this member calls must attest.
182
+ const labels = labelsFromEnv("AGENTMESH_SDK_LABELS");
183
+ const egressRequireLabels = labelsFromEnv("AGENTMESH_SDK_EGRESS_REQUIRE_LABELS");
184
+ const routerSelector = labelsFromEnv("AGENTMESH_SDK_ROUTER_SELECTOR");
185
+ const routerPrefer = labelsFromEnv("AGENTMESH_SDK_ROUTER_PREFER");
186
+ const routerCount = process.env.AGENTMESH_SDK_ROUTER_COUNT;
187
+ const redialBackoffs = (process.env.AGENTMESH_SDK_REDIAL_BACKOFF_SECONDS ?? "")
188
+ .split(",")
189
+ .filter((s) => s.trim() !== "")
190
+ .map((s) => Number(s.trim()) * 1000);
191
+ // AGENTMESH_SDK_RELAY_CHECK_SECONDS shortens how often the relay reservation is
192
+ // checked, so a test that moves a router sees the member follow it within
193
+ // its budget.
194
+ const relayCheck = process.env.AGENTMESH_SDK_RELAY_CHECK_SECONDS;
195
+ const relayOptions = relayCheck !== undefined && relayCheck !== "" ? { relayCheckIntervalMs: Number(relayCheck) * 1000 } : {};
196
+ const mesh = await AgentMesh.enroll({
197
+ controlPlaneUrl,
198
+ allowInsecure,
199
+ stateDir,
200
+ bootstrapTokenPath,
201
+ jwtPath,
202
+ pollIntervalMs: 200,
203
+ ...(Object.keys(labels).length > 0 ? { labels } : {}),
204
+ });
205
+ // The test drives every pull itself; only gossip events bring one forward.
206
+ // AGENTMESH_SDK_ROUTERS, peer IDs, joins through those routers only, so the test
207
+ // can put two members on different routers.
208
+ const only = new Set((process.env.AGENTMESH_SDK_ROUTERS ?? "").split(",").filter((id) => id !== ""));
209
+ const routerAddresses = only.size === 0 ? undefined : mesh.credential.routerAddresses.filter((a) => [...only].some((id) => a.endsWith(`/p2p/${id}`)));
210
+ const session = await mesh.join({
211
+ listenAddrs,
212
+ ...(routerAddresses !== undefined ? { routerAddresses } : {}),
213
+ ...(routerCount !== undefined && routerCount !== "" ? { routers: Number(routerCount) } : {}),
214
+ ...(Object.keys(routerSelector).length > 0 ? { routerSelector } : {}),
215
+ ...(Object.keys(routerPrefer).length > 0 ? { routerPrefer } : {}),
216
+ ...(redialBackoffs.length > 0 ? { routerRedialBackoffsMs: redialBackoffs } : {}),
217
+ ...(Object.keys(egressRequireLabels).length > 0 ? { egressRequireLabels } : {}),
218
+ ...relayOptions,
219
+ signal: AbortSignal.timeout(20_000),
220
+ controlPlaneSyncIntervalMs: 0,
221
+ controlPlaneSyncJitterMs: 0,
222
+ });
223
+ emit({
224
+ sdk: "js",
225
+ peer_id: session.peerId,
226
+ routers: session.routers.map((r) => ({ peer_id: r.peerId, addr: r.addr.toString(), roles: r.credential.roles })),
227
+ relay_addresses: session.relayAddresses.map((ma) => ma.toString()),
228
+ direct_addresses: session.addresses.filter((ma) => !ma.toString().includes("/p2p-circuit")).map((ma) => ma.toString()),
229
+ biscuit: Buffer.from(mesh.credential.biscuit).toString("base64"),
230
+ trusted_keys: mesh.credential.controlPlaneKeys.map((k) => Buffer.from(k).toString("hex")),
231
+ });
232
+ for await (const line of createInterface({ input: process.stdin })) {
233
+ if (line.trim() === "") {
234
+ continue;
235
+ }
236
+ let command;
237
+ try {
238
+ command = JSON.parse(line);
239
+ }
240
+ catch (err) {
241
+ emit({ ok: false, error: `not JSON: ${err instanceof Error ? err.message : String(err)}` });
242
+ continue;
243
+ }
244
+ if (command.cmd === "quit") {
245
+ emit({ cmd: "quit", ok: true });
246
+ break;
247
+ }
248
+ emit(await handle(session, command));
249
+ }
250
+ // Leaving must not depend on every peer closing its side promptly.
251
+ await Promise.race([session.close(), new Promise((resolve) => setTimeout(resolve, 5_000).unref())]);
252
+ process.exit(0);
253
+ }
254
+ main().catch((err) => {
255
+ process.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
256
+ process.exit(1);
257
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ // Copyright 2026 Google LLC
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // Conformance runner driven by tests/integration/sdk_enroll_test.go: enrolls
15
+ // against a real control plane, refreshes, reloads from disk and reports
16
+ // what it holds as JSON on stdout for the Go side to verify. The Python SDK
17
+ // ships the same runner (python -m agent_mesh.conformance) with the same
18
+ // environment and output, so one Go test checks both.
19
+ //
20
+ // AGENTMESH_CONTROL_PLANE_URL base URL of the control plane
21
+ // AGENTMESH_BOOTSTRAP_TOKEN_PATH file holding a bootstrap token
22
+ // AGENTMESH_SDK_STATE_DIR directory for identity and credential
23
+ // AGENTMESH_INSECURE_CONTROL_PLANE "1" to accept plaintext http:// off loopback
24
+ import { AgentMesh } from "./mesh.js";
25
+ const hex = (b) => Buffer.from(b).toString("hex");
26
+ const b64 = (b) => Buffer.from(b).toString("base64");
27
+ function requireEnv(name) {
28
+ const v = process.env[name];
29
+ if (!v) {
30
+ throw new Error(`${name} is required`);
31
+ }
32
+ return v;
33
+ }
34
+ async function main() {
35
+ const controlPlaneUrl = requireEnv("AGENTMESH_CONTROL_PLANE_URL");
36
+ const bootstrapTokenPath = requireEnv("AGENTMESH_BOOTSTRAP_TOKEN_PATH");
37
+ const stateDir = requireEnv("AGENTMESH_SDK_STATE_DIR");
38
+ const allowInsecure = process.env.AGENTMESH_INSECURE_CONTROL_PLANE === "1";
39
+ const mesh = await AgentMesh.enroll({ controlPlaneUrl, allowInsecure, stateDir, bootstrapTokenPath, pollIntervalMs: 200 });
40
+ const enrolled = mesh.credential;
41
+ const reloaded = await AgentMesh.load({ allowInsecure, stateDir });
42
+ const refreshed = await reloaded.refresh();
43
+ process.stdout.write(JSON.stringify({
44
+ sdk: "js",
45
+ peer_id: mesh.peerId,
46
+ public_key: hex(mesh.identity.publicKeyRaw),
47
+ biscuit: b64(enrolled.biscuit),
48
+ expiration: enrolled.expiration,
49
+ control_plane_keys: enrolled.controlPlaneKeys.map(hex),
50
+ router_addresses: enrolled.routerAddresses,
51
+ reloaded_peer_id: reloaded.peerId,
52
+ refreshed_biscuit: b64(refreshed.biscuit),
53
+ refreshed_expiration: refreshed.expiration,
54
+ auth_frame: b64(reloaded.authFrame("mcp://echo")),
55
+ }) + "\n");
56
+ }
57
+ main().catch((err) => {
58
+ process.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
59
+ process.exit(1);
60
+ });
@@ -0,0 +1,123 @@
1
+ import { type ControlPlaneInfoResponse, type KeysResponse } from "./gen/agentmesh_pb.ts";
2
+ import type { Identity } from "./identity.ts";
3
+ export declare const PROTOBUF_CONTENT_TYPE = "application/x-protobuf";
4
+ export declare const HEADER_CHALLENGE_TIMESTAMP = "X-Mesh-Challenge-Ts";
5
+ export declare const HEADER_CHALLENGE_SIGNATURE = "X-Mesh-Challenge-Sig";
6
+ export declare const STALE_CHALLENGE_TIMESTAMP_MESSAGE = "stale or invalid challenge timestamp";
7
+ /** The role a plain mesh member enrolls with (api.RoleNode). */
8
+ export declare const ROLE_NODE = "mesh:role:node";
9
+ /** How far a signed /keys response's timestamp may drift from our clock. */
10
+ export declare const KEYS_RESPONSE_FRESHNESS_MS: number;
11
+ /** A non-2xx answer from the control plane. */
12
+ export declare class ControlPlaneError extends Error {
13
+ readonly status: number;
14
+ readonly body: string;
15
+ constructor(path: string, status: number, body: string);
16
+ }
17
+ /** The control plane answered, and the answer is a refusal. */
18
+ export declare class EnrollmentRejectedError extends Error {
19
+ constructor(message: string);
20
+ }
21
+ /**
22
+ * The /keys answer is signed by no key this member trusts: every key it
23
+ * holds has left the control plane's set, so its credential cannot be
24
+ * verified or refreshed either.
25
+ */
26
+ export declare class KeysNotTrustedError extends Error {
27
+ constructor();
28
+ }
29
+ /** Plaintext http:// to a host that is not loopback; see api.ValidateControlPlaneTransport. */
30
+ export declare class InsecureControlPlaneURLError extends Error {
31
+ constructor(url: string);
32
+ }
33
+ export interface ControlPlaneClientOptions {
34
+ /** Base URL, e.g. https://mesh.example.com. */
35
+ url: string;
36
+ /** Accept plaintext http:// to a non-loopback host. Off by default. */
37
+ allowInsecure?: boolean;
38
+ /** Per-request timeout. */
39
+ timeoutMs?: number;
40
+ /** Injection point for tests. Defaults to the global fetch. */
41
+ fetch?: typeof fetch;
42
+ /** Injection point for clock-skew tests; returns unix milliseconds. Defaults to Date.now. */
43
+ now?: () => number;
44
+ }
45
+ /** What an approved enrollment hands the caller. */
46
+ export interface Enrollment {
47
+ biscuit: Uint8Array;
48
+ /** Unix seconds at which the biscuit expires. */
49
+ expiration: number;
50
+ controlPlanePublicKey: Uint8Array;
51
+ routerAddresses: string[];
52
+ }
53
+ export interface EnrollBootstrapParams {
54
+ identity: Identity;
55
+ bootstrapToken: string;
56
+ /** Must equal the role the bootstrap token was minted for. Defaults to ROLE_NODE. */
57
+ role?: string;
58
+ labels?: Record<string, string>;
59
+ /** Overrides the control plane's suggested poll interval while pending. */
60
+ pollIntervalMs?: number;
61
+ /** Bounds the wait for an operator to approve a pending enrollment. */
62
+ signal?: AbortSignal;
63
+ }
64
+ export interface RegisterParams {
65
+ identity: Identity;
66
+ jwt: string;
67
+ role?: string;
68
+ labels?: Record<string, string>;
69
+ }
70
+ export interface RefreshParams {
71
+ identity: Identity;
72
+ /** The biscuit currently held; only the last one issued is redeemable. */
73
+ biscuit: Uint8Array;
74
+ /** Optional fresh platform/OIDC JWT for continuous attestation on refresh. */
75
+ jwt?: string | undefined;
76
+ }
77
+ export interface PolicyRulesParams {
78
+ identity: Identity;
79
+ biscuit: Uint8Array;
80
+ }
81
+ export interface RefreshResult {
82
+ biscuit: Uint8Array;
83
+ /** Unix seconds. */
84
+ expiration: number;
85
+ }
86
+ /** Mirrors api.ValidateControlPlaneTransport: https, or http to loopback only. */
87
+ export declare function validateControlPlaneURL(rawUrl: string, allowInsecure?: boolean): URL;
88
+ /**
89
+ * Returns the key set if it is fresh and at least one listed key is already
90
+ * trusted and its signature verifies. Mirrors api.VerifyKeysResponse.
91
+ */
92
+ export declare function verifyKeysResponse(resp: KeysResponse, trusted: Uint8Array[], nowMs?: number): Uint8Array[];
93
+ export declare class ControlPlaneClient {
94
+ #private;
95
+ readonly url: URL;
96
+ constructor(options: ControlPlaneClientOptions);
97
+ /** GET /info: OIDC settings, router addresses and the ban list. Unauthenticated. */
98
+ info(): Promise<ControlPlaneInfoResponse>;
99
+ /**
100
+ * GET /keys: every signing key the control plane currently trusts,
101
+ * verified against a key the caller already trusts (the enrollment key).
102
+ */
103
+ keys(trusted: Uint8Array[]): Promise<Uint8Array[]>;
104
+ /**
105
+ * POST /enroll with a bootstrap token, then GET /enroll/status until an
106
+ * operator approves the request if the mesh is not on auto-approve.
107
+ */
108
+ enrollBootstrap(params: EnrollBootstrapParams): Promise<Enrollment>;
109
+ /** POST /register with an OIDC ID token. */
110
+ register(params: RegisterParams): Promise<Enrollment>;
111
+ /**
112
+ * POST /refresh: trades the biscuit for a fresh one. The old one is
113
+ * spent by this call; callers must persist the result before using it.
114
+ */
115
+ refresh(params: RefreshParams): Promise<RefreshResult>;
116
+ /**
117
+ * GET /policies: the mesh policy as the Datalog rules a provider adds to
118
+ * its authorizer, one per entry, rendered by the control plane. The text
119
+ * is the contract; nothing here derives rules from roles and bindings.
120
+ */
121
+ policyRules(identity: Identity, biscuit: Uint8Array): Promise<string[]>;
122
+ policyRules(params: PolicyRulesParams): Promise<string[]>;
123
+ }