@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,31 @@
1
+ /** The libp2p protobuf encoding of an ed25519 public key. */
2
+ export declare function libp2pPublicKey(publicKeyRaw: Uint8Array): Uint8Array;
3
+ /** The peer ID libp2p derives from an ed25519 public key (base58btc, "12D3Koo..."). */
4
+ export declare function peerIdFromPublicKey(publicKeyRaw: Uint8Array): string;
5
+ /**
6
+ * The base58btc form of a peer ID written in any encoding libp2p accepts
7
+ * (base58btc multihash, CIDv1). Every key, ban set and comparison in Agent Mesh is
8
+ * on this form, as peer.ID.String() in Go; a string read off the wire or
9
+ * from a caller goes through here before it is used as one. Throws when the
10
+ * text is not a peer ID at all.
11
+ */
12
+ export declare function canonicalPeerId(text: string): string;
13
+ export declare class Identity {
14
+ #private;
15
+ /** Raw 32-byte ed25519 public key. */
16
+ readonly publicKeyRaw: Uint8Array;
17
+ readonly peerId: string;
18
+ private constructor();
19
+ static generate(): Identity;
20
+ static fromSeed(seed: Uint8Array): Identity;
21
+ /** Loads the libp2p protobuf private key encoding, the format agentmesh-node persists. */
22
+ static fromLibp2pPrivateKey(bytes: Uint8Array): Identity;
23
+ /** The libp2p protobuf private key encoding (seed || public key). */
24
+ toLibp2pPrivateKey(): Uint8Array;
25
+ /** The libp2p protobuf public key encoding, what the control plane stores. */
26
+ get libp2pPublicKey(): Uint8Array;
27
+ sign(data: Uint8Array): Uint8Array;
28
+ verify(data: Uint8Array, signature: Uint8Array): boolean;
29
+ }
30
+ /** Verifies an ed25519 signature with a raw 32-byte public key. */
31
+ export declare function verifyEd25519(publicKeyRaw: Uint8Array, data: Uint8Array, signature: Uint8Array): boolean;
@@ -0,0 +1,125 @@
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
+ // A mesh identity is an ed25519 key pair. Its peer ID is the one libp2p
15
+ // derives, so the same key works in the SDK, in agentmesh-node and on the wire.
16
+ // The arithmetic is @noble/curves, the implementation libp2p itself uses,
17
+ // so an identity is built and used the same way in Node and in a browser.
18
+ import { ed25519 } from "@noble/curves/ed25519.js";
19
+ import { peerIdFromString } from "@libp2p/peer-id";
20
+ import { encodeBase58 } from "./base58.js";
21
+ import { bytesEqual, concatBytes as concat } from "./bytes.js";
22
+ const PUBLIC_KEY_SIZE = 32;
23
+ const SEED_SIZE = 32;
24
+ const SIGNATURE_SIZE = 64;
25
+ // libp2p crypto.proto PublicKey{Type: Ed25519 (1), Data: <32 bytes>}.
26
+ const LIBP2P_PUBLIC_KEY_PREFIX = Uint8Array.of(0x08, 0x01, 0x12, 0x20);
27
+ // libp2p crypto.proto PrivateKey{Type: Ed25519 (1), Data: <seed || public>}.
28
+ const LIBP2P_PRIVATE_KEY_PREFIX = Uint8Array.of(0x08, 0x01, 0x12, 0x40);
29
+ // multihash: identity function (0x00), digest length 36.
30
+ const IDENTITY_MULTIHASH_PREFIX = Uint8Array.of(0x00, 0x24);
31
+ function startsWith(bytes, prefix) {
32
+ return prefix.every((b, i) => bytes[i] === b);
33
+ }
34
+ // RFC 8032 verification, as Go crypto/ed25519 and node:crypto do it; the
35
+ // looser ZIP 215 rules noble defaults to accept signatures those reject.
36
+ function verifyRaw(publicKeyRaw, data, signature) {
37
+ if (publicKeyRaw.length !== PUBLIC_KEY_SIZE || signature.length !== SIGNATURE_SIZE) {
38
+ return false;
39
+ }
40
+ try {
41
+ return ed25519.verify(signature, data, publicKeyRaw, { zip215: false });
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /** The libp2p protobuf encoding of an ed25519 public key. */
48
+ export function libp2pPublicKey(publicKeyRaw) {
49
+ if (publicKeyRaw.length !== PUBLIC_KEY_SIZE) {
50
+ throw new Error(`ed25519 public key must be ${PUBLIC_KEY_SIZE} bytes, got ${publicKeyRaw.length}`);
51
+ }
52
+ return concat(LIBP2P_PUBLIC_KEY_PREFIX, publicKeyRaw);
53
+ }
54
+ /** The peer ID libp2p derives from an ed25519 public key (base58btc, "12D3Koo..."). */
55
+ export function peerIdFromPublicKey(publicKeyRaw) {
56
+ return encodeBase58(concat(IDENTITY_MULTIHASH_PREFIX, libp2pPublicKey(publicKeyRaw)));
57
+ }
58
+ /**
59
+ * The base58btc form of a peer ID written in any encoding libp2p accepts
60
+ * (base58btc multihash, CIDv1). Every key, ban set and comparison in Agent Mesh is
61
+ * on this form, as peer.ID.String() in Go; a string read off the wire or
62
+ * from a caller goes through here before it is used as one. Throws when the
63
+ * text is not a peer ID at all.
64
+ */
65
+ export function canonicalPeerId(text) {
66
+ try {
67
+ return peerIdFromString(text).toString();
68
+ }
69
+ catch (err) {
70
+ throw new Error(`${JSON.stringify(text)} is not a peer ID: ${err instanceof Error ? err.message : String(err)}`);
71
+ }
72
+ }
73
+ export class Identity {
74
+ #seed;
75
+ /** Raw 32-byte ed25519 public key. */
76
+ publicKeyRaw;
77
+ peerId;
78
+ constructor(seed) {
79
+ if (seed.length !== SEED_SIZE) {
80
+ throw new Error(`ed25519 seed must be ${SEED_SIZE} bytes, got ${seed.length}`);
81
+ }
82
+ this.#seed = new Uint8Array(seed);
83
+ this.publicKeyRaw = ed25519.getPublicKey(this.#seed);
84
+ this.peerId = peerIdFromPublicKey(this.publicKeyRaw);
85
+ }
86
+ static generate() {
87
+ const seed = new Uint8Array(SEED_SIZE);
88
+ crypto.getRandomValues(seed);
89
+ return new Identity(seed);
90
+ }
91
+ static fromSeed(seed) {
92
+ return new Identity(seed);
93
+ }
94
+ /** Loads the libp2p protobuf private key encoding, the format agentmesh-node persists. */
95
+ static fromLibp2pPrivateKey(bytes) {
96
+ if (bytes.length !== LIBP2P_PRIVATE_KEY_PREFIX.length + 64 || !startsWith(bytes, LIBP2P_PRIVATE_KEY_PREFIX)) {
97
+ throw new Error("not a libp2p ed25519 private key");
98
+ }
99
+ const seed = bytes.subarray(LIBP2P_PRIVATE_KEY_PREFIX.length, LIBP2P_PRIVATE_KEY_PREFIX.length + SEED_SIZE);
100
+ const pub = bytes.subarray(LIBP2P_PRIVATE_KEY_PREFIX.length + SEED_SIZE);
101
+ const id = new Identity(seed);
102
+ if (!bytesEqual(pub, id.publicKeyRaw)) {
103
+ throw new Error("libp2p private key: public half does not match the seed");
104
+ }
105
+ return id;
106
+ }
107
+ /** The libp2p protobuf private key encoding (seed || public key). */
108
+ toLibp2pPrivateKey() {
109
+ return concat(LIBP2P_PRIVATE_KEY_PREFIX, this.#seed, this.publicKeyRaw);
110
+ }
111
+ /** The libp2p protobuf public key encoding, what the control plane stores. */
112
+ get libp2pPublicKey() {
113
+ return libp2pPublicKey(this.publicKeyRaw);
114
+ }
115
+ sign(data) {
116
+ return ed25519.sign(data, this.#seed);
117
+ }
118
+ verify(data, signature) {
119
+ return verifyRaw(this.publicKeyRaw, data, signature);
120
+ }
121
+ }
122
+ /** Verifies an ed25519 signature with a raw 32-byte public key. */
123
+ export function verifyEd25519(publicKeyRaw, data, signature) {
124
+ return verifyRaw(publicKeyRaw, data, signature);
125
+ }
@@ -0,0 +1,18 @@
1
+ export { AgentMesh, CredentialRetiredError, type AgentMeshOptions, type ControlPlaneSync, type EnrollOptions, type JwtSource } from "./mesh.ts";
2
+ export { Identity, canonicalPeerId, peerIdFromPublicKey, libp2pPublicKey, verifyEd25519 } from "./identity.ts";
3
+ export { ControlPlaneClient, ControlPlaneError, EnrollmentRejectedError, InsecureControlPlaneURLError, KeysNotTrustedError, ROLE_NODE, validateControlPlaneURL, verifyKeysResponse, type ControlPlaneClientOptions, type Enrollment, type EnrollBootstrapParams, type PolicyRulesParams, type RegisterParams, type RefreshParams, type RefreshResult, } from "./controlplane.ts";
4
+ export { attenuateCredential, credentialFromJSON, credentialTimeToLiveSeconds, credentialToJSON, decodeAuthResponse, encodeAuthFrame, sealCredential, withCredentialMethods, type MeshCredential, } from "./credential.ts";
5
+ export { egressChallenge, enrollChallenge, enrollStatusChallenge, nodesCatalogChallenge, policiesChallenge, refreshChallenge, registerChallenge, revocationsChallenge, } from "./challenges.ts";
6
+ export { DEFAULT_ROUTERS, MeshSession, ROUTER_REDIAL_BACKOFFS_MS, candidatesFromRouterInfos, selectRouters, type AdmittedRouter, type DiscoveredProvider, type JoinOptions, type Peer, type RouterCandidate, type SelectRoutersOptions, type ToolCallResult, } from "./session.ts";
7
+ export { BiscuitVerificationError, ROLE_ROUTER, attenuateBiscuit, requireRole, sealBiscuit, verifyPeerBiscuit, type VerifiedBiscuit, } from "./biscuit.ts";
8
+ export { decodeTARBlockPayload, effectiveTARExpiration, encodeTARBlockFact, encodeTARBlockPayload, evaluateTaskRules, matchHTTPPath, matchServicePattern, matchTaskRule, parseTARBlockSource, validateHTTPGrantPath, validateServicePattern, validateTaskAuthorizationRule, validateTaskRule, type TaskRequestContext, } from "./tar.ts";
9
+ export { AUTH_HANDLER_OPTIONS, AUTH_PROTOCOL, GOAWAY_MAX_RETRY_AFTER_MS, GOAWAY_PROTOCOL, GOAWAY_STREAM_TIMEOUT_MS, MAX_GOAWAY_MESSAGE_BYTES, MCP_PROTOCOL, ROUTER_SHUN_DURATION_MS, AuthRejectedError, authenticateWithPeer, authStreamHandler, clampGoAwayRetryAfterMs, goAwayReasonName, goAwayStreamHandler, type GoAwayServerOptions, } from "./auth.ts";
10
+ export { createMeshHost, type MeshHost, type MeshHostOptions } from "./host.ts";
11
+ export { DIRECTORY_PROTOCOL, DirectoryError, isServiceType, parseServiceTarget, peerKey, serviceKey, type ServiceType } from "./discovery.ts";
12
+ export { LabelsNotSatisfiedError, StreamTransport, openMCPSession, requireEgressLabels, requireLabels, type MCPSession, type MCPSessionOptions } from "./mcp.ts";
13
+ export { AuthorizationError, authorizeCaller, type AuthorizeRequest, type ProviderAuthorizerOptions } from "./authorizer.ts";
14
+ export { DEFAULT_A2A_NAME, AGENT_CARD_PATH, HTTP_HANDLER_OPTIONS, HTTP_PROTOCOL, MESH_PATH_PREFIX, a2aEndpoint, admitIngress, fetchOverStream, httpIngressHandler, httpRequestOverStream, meshHTTPTarget, meshURL, rewriteAgentCard, splitMeshURL, type A2AEndpoint, type A2AEndpointSpec, type HTTPHandler, type HTTPRequestOptions, type HTTPResponse, type HTTPStreamOptions, type NodeRequestListener, type ProviderOptions, } from "./libp2p-http.ts";
15
+ export { ingressHandler } from "./platform/ingress.ts";
16
+ export type { StateStore } from "./platform/types.ts";
17
+ export { BASELINE_DATALOG } from "./gen/datalog.ts";
18
+ export { BanSet, EVENT_FRESHNESS_MS, GOSSIP_EVENTS_TOPIC, verifyMeshEvent, type VerifiedMeshEvent } from "./sync.ts";
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
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
+ export { AgentMesh, CredentialRetiredError } from "./mesh.js";
15
+ export { Identity, canonicalPeerId, peerIdFromPublicKey, libp2pPublicKey, verifyEd25519 } from "./identity.js";
16
+ export { ControlPlaneClient, ControlPlaneError, EnrollmentRejectedError, InsecureControlPlaneURLError, KeysNotTrustedError, ROLE_NODE, validateControlPlaneURL, verifyKeysResponse, } from "./controlplane.js";
17
+ export { attenuateCredential, credentialFromJSON, credentialTimeToLiveSeconds, credentialToJSON, decodeAuthResponse, encodeAuthFrame, sealCredential, withCredentialMethods, } from "./credential.js";
18
+ export { egressChallenge, enrollChallenge, enrollStatusChallenge, nodesCatalogChallenge, policiesChallenge, refreshChallenge, registerChallenge, revocationsChallenge, } from "./challenges.js";
19
+ export { DEFAULT_ROUTERS, MeshSession, ROUTER_REDIAL_BACKOFFS_MS, candidatesFromRouterInfos, selectRouters, } from "./session.js";
20
+ export { BiscuitVerificationError, ROLE_ROUTER, attenuateBiscuit, requireRole, sealBiscuit, verifyPeerBiscuit, } from "./biscuit.js";
21
+ export { decodeTARBlockPayload, effectiveTARExpiration, encodeTARBlockFact, encodeTARBlockPayload, evaluateTaskRules, matchHTTPPath, matchServicePattern, matchTaskRule, parseTARBlockSource, validateHTTPGrantPath, validateServicePattern, validateTaskAuthorizationRule, validateTaskRule, } from "./tar.js";
22
+ export { AUTH_HANDLER_OPTIONS, AUTH_PROTOCOL, GOAWAY_MAX_RETRY_AFTER_MS, GOAWAY_PROTOCOL, GOAWAY_STREAM_TIMEOUT_MS, MAX_GOAWAY_MESSAGE_BYTES, MCP_PROTOCOL, ROUTER_SHUN_DURATION_MS, AuthRejectedError, authenticateWithPeer, authStreamHandler, clampGoAwayRetryAfterMs, goAwayReasonName, goAwayStreamHandler, } from "./auth.js";
23
+ export { createMeshHost } from "./host.js";
24
+ export { DIRECTORY_PROTOCOL, DirectoryError, isServiceType, parseServiceTarget, peerKey, serviceKey } from "./discovery.js";
25
+ export { LabelsNotSatisfiedError, StreamTransport, openMCPSession, requireEgressLabels, requireLabels } from "./mcp.js";
26
+ export { AuthorizationError, authorizeCaller } from "./authorizer.js";
27
+ export { DEFAULT_A2A_NAME, AGENT_CARD_PATH, HTTP_HANDLER_OPTIONS, HTTP_PROTOCOL, MESH_PATH_PREFIX, a2aEndpoint, admitIngress, fetchOverStream, httpIngressHandler, httpRequestOverStream, meshHTTPTarget, meshURL, rewriteAgentCard, splitMeshURL, } from "./libp2p-http.js";
28
+ export { ingressHandler } from "./platform/ingress.js";
29
+ export { BASELINE_DATALOG } from "./gen/datalog.js";
30
+ export { BanSet, EVENT_FRESHNESS_MS, GOSSIP_EVENTS_TOPIC, verifyMeshEvent } from "./sync.js";
@@ -0,0 +1,17 @@
1
+ import type { Stream, StreamHandler } from "@libp2p/interface";
2
+ import { Duplex } from "node:stream";
3
+ import { type A2AEndpoint, type ProviderOptions } from "./libp2p-http.ts";
4
+ interface StreamSocket extends Duplex {
5
+ remotePeer: string;
6
+ }
7
+ /**
8
+ * Bridges a libp2p stream to a Node Duplex so Node's own HTTP parser and
9
+ * client can run over it.
10
+ */
11
+ export declare function streamToNodeDuplex(stream: Stream, remotePeer: string): StreamSocket;
12
+ /**
13
+ * Server side of /libp2p-http on Node: a listener target is served by Node's
14
+ * HTTP server on the stream, any other by the runtime-neutral ingress.
15
+ */
16
+ export declare function nodeIngressHandler(endpoint: A2AEndpoint, options: ProviderOptions): StreamHandler;
17
+ export {};
@@ -0,0 +1,116 @@
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
+ import http from "node:http";
15
+ import { Duplex } from "node:stream";
16
+ import { AUTH_HANDSHAKE_TIMEOUT_MS } from "./auth.js";
17
+ import { HEADER_PEER_ID, HEADER_MESH_BISCUIT, HEADER_MESH_NO_TRAILING_SLASH, admitIngress, httpIngressHandler, } from "./libp2p-http.js";
18
+ /**
19
+ * Bridges a libp2p stream to a Node Duplex so Node's own HTTP parser and
20
+ * client can run over it.
21
+ */
22
+ export function streamToNodeDuplex(stream, remotePeer) {
23
+ const duplex = new Duplex({
24
+ read() {
25
+ stream.resume();
26
+ },
27
+ write(chunk, _encoding, callback) {
28
+ if (stream.send(chunk)) {
29
+ callback();
30
+ }
31
+ else {
32
+ stream.addEventListener("drain", () => callback(), { once: true });
33
+ }
34
+ },
35
+ final(callback) {
36
+ stream.close().then(() => callback(), (err) => callback(err));
37
+ },
38
+ destroy(err, callback) {
39
+ if (err !== null) {
40
+ stream.abort(err);
41
+ }
42
+ else {
43
+ void stream.close().catch(() => { });
44
+ }
45
+ callback(err);
46
+ },
47
+ });
48
+ duplex.remotePeer = remotePeer;
49
+ stream.addEventListener("message", (evt) => {
50
+ if (!duplex.push(Buffer.from(evt.data.subarray()))) {
51
+ stream.pause();
52
+ }
53
+ });
54
+ const end = () => {
55
+ if (!duplex.readableEnded) {
56
+ duplex.push(null);
57
+ }
58
+ };
59
+ stream.addEventListener("remoteCloseWrite", end);
60
+ stream.addEventListener("close", end);
61
+ return duplex;
62
+ }
63
+ function nodeHeaders(req) {
64
+ const headers = new Headers();
65
+ for (const [name, value] of Object.entries(req.headers)) {
66
+ for (const v of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
67
+ headers.append(name, v);
68
+ }
69
+ }
70
+ return headers;
71
+ }
72
+ async function serveListener(req, res, endpoint, listener, options) {
73
+ const remotePeer = req.socket.remotePeer;
74
+ const admission = await admitIngress({ method: req.method ?? "GET", target: req.url ?? "/", headers: nodeHeaders(req), remotePeer }, endpoint, options);
75
+ if ("status" in admission) {
76
+ res.writeHead(admission.status, { "content-type": "text/plain; charset=utf-8" });
77
+ res.end(admission.text + "\n");
78
+ return;
79
+ }
80
+ // The listener sees the request as a backend behind agentmesh-node would: the
81
+ // path relative to the service, the verified caller, never the biscuit.
82
+ // X-Peer-Id is set, not added, so an inbound value cannot pose as the
83
+ // verified peer.
84
+ req.url = admission.path;
85
+ delete req.headers[HEADER_MESH_BISCUIT];
86
+ req.headers[HEADER_PEER_ID] = remotePeer;
87
+ if (admission.noTrailingSlash) {
88
+ req.headers[HEADER_MESH_NO_TRAILING_SLASH] = "true";
89
+ }
90
+ else {
91
+ delete req.headers[HEADER_MESH_NO_TRAILING_SLASH];
92
+ }
93
+ listener(req, res);
94
+ }
95
+ /**
96
+ * Server side of /libp2p-http on Node: a listener target is served by Node's
97
+ * HTTP server on the stream, any other by the runtime-neutral ingress.
98
+ */
99
+ export function nodeIngressHandler(endpoint, options) {
100
+ if (!("listener" in endpoint.target)) {
101
+ return httpIngressHandler(endpoint, options);
102
+ }
103
+ const listener = endpoint.target.listener;
104
+ const server = http.createServer({ keepAlive: false }, (req, res) => {
105
+ void serveListener(req, res, endpoint, listener, options).catch((err) => {
106
+ if (!res.headersSent) {
107
+ res.writeHead(500, { "content-type": "text/plain" });
108
+ }
109
+ res.end(`ingress error: ${err instanceof Error ? err.message : String(err)}\n`);
110
+ });
111
+ });
112
+ server.headersTimeout = AUTH_HANDSHAKE_TIMEOUT_MS;
113
+ return (stream, connection) => {
114
+ server.emit("connection", streamToNodeDuplex(stream, connection.remotePeer.toString()));
115
+ };
116
+ }
@@ -0,0 +1,165 @@
1
+ import type { Connection, StreamHandler } from "@libp2p/interface";
2
+ import { type ProviderAuthorizerOptions } from "./authorizer.ts";
3
+ import type { VerifiedBiscuit } from "./biscuit.ts";
4
+ /** go-libp2p-http's protocol: plain HTTP/1.1 on a stream, one request per stream. */
5
+ export declare const HTTP_PROTOCOL = "/libp2p-http";
6
+ /** Headers of the mesh HTTP datapath (api/network.go). */
7
+ export declare const HEADER_MESH_BISCUIT = "x-mesh-biscuit";
8
+ export declare const HEADER_PEER_ID = "x-peer-id";
9
+ export declare const HEADER_MESH_NO_TRAILING_SLASH = "x-mesh-no-trailing-slash";
10
+ /** Options for libp2p.handle() so the agent is reachable over relayed connections. */
11
+ export declare const HTTP_HANDLER_OPTIONS: {
12
+ runOnLimitedConnection: boolean;
13
+ };
14
+ /** The service name an agent answers under unless it picks another. */
15
+ export declare const DEFAULT_A2A_NAME = "agent";
16
+ /**
17
+ * The path prefix of a mesh URL, http://mesh/mesh/<peer-id>/<type>/<name>/<path>:
18
+ * the shape of agentmesh-node's egress proxy and of an agent card rewritten for the
19
+ * mesh, by agentmesh-node or by this SDK. The host is ignored; the peer ID is in the
20
+ * path because URL parsers lowercase the host and a peer ID is case-sensitive.
21
+ */
22
+ export declare const MESH_PATH_PREFIX = "/mesh/";
23
+ /** The well-known agent card location (A2A spec / RFC 8615). */
24
+ export declare const AGENT_CARD_PATH = ".well-known/agent-card.json";
25
+ /** Largest request body the ingress reads whole for a handler or url target. */
26
+ export declare const MAX_INGRESS_BODY_BYTES: number;
27
+ /** A fetch-style handler in this process. */
28
+ export type HTTPHandler = (request: Request, caller: VerifiedBiscuit) => Promise<Response> | Response;
29
+ /**
30
+ * A request listener of the runtime's own HTTP server, such as an Express
31
+ * app with the A2A SDK's handlers mounted. On Node it is
32
+ * (req: http.IncomingMessage, res: http.ServerResponse) => void, served by
33
+ * libp2p-http-node.ts; a browser has no such server and answers 501.
34
+ */
35
+ export type NodeRequestListener = (req: any, res: any) => void;
36
+ /**
37
+ * This member's agent as other members reach it: `a2a://<name>`, answered by
38
+ * exactly one of url (an A2A server beside this process), handler or
39
+ * listener (in this process). Authorized requests arrive with the biscuit
40
+ * header stripped, X-Peer-Id naming the verified caller and the path
41
+ * relative to /a2a/<name>, as agentmesh-node forwards them. The endpoint is not
42
+ * announced anywhere; a caller reaches it by peer ID.
43
+ */
44
+ export interface A2AEndpointSpec {
45
+ name?: string;
46
+ url?: string;
47
+ handler?: HTTPHandler;
48
+ listener?: NodeRequestListener;
49
+ }
50
+ export interface A2AEndpoint {
51
+ name: string;
52
+ service: string;
53
+ target: {
54
+ url: string;
55
+ } | {
56
+ handler: HTTPHandler;
57
+ } | {
58
+ listener: NodeRequestListener;
59
+ };
60
+ }
61
+ export declare function a2aEndpoint(spec: A2AEndpointSpec): A2AEndpoint;
62
+ export interface ProviderOptions extends ProviderAuthorizerOptions {
63
+ /** Peers the control plane has banned; refused before their token is looked at. */
64
+ isBanned?(peerId: string): boolean;
65
+ /** Called after a caller is authorized, e.g. for the session's admitted set. */
66
+ onAuthorized?(peerId: string, verified: VerifiedBiscuit, targetService: string): void;
67
+ }
68
+ /** What the ingress looks at before anything reaches the agent. */
69
+ export interface IngressRequest {
70
+ /** The request method as it came off the wire. */
71
+ method: string;
72
+ /** The request target as it came off the wire, path and query. */
73
+ target: string;
74
+ headers: Headers;
75
+ remotePeer: string;
76
+ }
77
+ /** A refusal, with the text the caller gets. */
78
+ export interface IngressRefusal {
79
+ status: number;
80
+ text: string;
81
+ }
82
+ /** An authorized request for the endpoint, with what the agent may see. */
83
+ export interface IngressAdmission {
84
+ verified: VerifiedBiscuit;
85
+ /** The path relative to the service, query included. */
86
+ path: string;
87
+ noTrailingSlash: boolean;
88
+ }
89
+ /**
90
+ * Server-side admission of /libp2p-http, as agentmesh-node's StartIngressServer:
91
+ * the path is /<type>/<name>[/<upstream>], the caller's biscuit is
92
+ * X-Mesh-Biscuit, and the request is authorized for <type>://<name> before
93
+ * anything is forwarded. Only the agent's own endpoint is answered; anything
94
+ * else is 404 after authorization, so an unauthorized caller learns nothing
95
+ * about it.
96
+ */
97
+ export declare function admitIngress(req: IngressRequest, endpoint: A2AEndpoint, options: ProviderOptions): Promise<IngressRefusal | IngressAdmission>;
98
+ /**
99
+ * The headers the agent sees: the request's own, less the datapath's, with
100
+ * X-Peer-Id set (not added) to the verified caller so an inbound value
101
+ * cannot pose as the peer.
102
+ */
103
+ export declare function agentHeaders(inbound: Headers, remotePeer: string, noTrailingSlash: boolean): Headers;
104
+ /** A plain-text refusal, as agentmesh-node's ingress writes one. */
105
+ export declare function refusalResponse(refusal: IngressRefusal): Response;
106
+ /**
107
+ * Server side of /libp2p-http for an endpoint answered by a handler in this
108
+ * process or an A2A server at a URL. One request per stream; the stream is
109
+ * closed once the response has been written.
110
+ */
111
+ export declare function httpIngressHandler(endpoint: A2AEndpoint, options: ProviderOptions): StreamHandler;
112
+ /** The request target for a service on a peer: /<type>/<name>/<path>. */
113
+ export declare function meshHTTPTarget(targetService: string, path?: string): string;
114
+ /**
115
+ * The URL a fetch bound to a session (session.fetch()) takes for a service
116
+ * on a peer: http://mesh/mesh/<peer-id>/<type>/<name>/<path>.
117
+ */
118
+ export declare function meshURL(peerId: string, targetService: string, path?: string): string;
119
+ /** (peer ID, request target) of a mesh URL; the target is what the peer's ingress takes. */
120
+ export declare function splitMeshURL(url: URL): {
121
+ peerId: string;
122
+ target: string;
123
+ };
124
+ /**
125
+ * An agent card rebuilt for the mesh, as agentmesh-node's egress proxy serves it:
126
+ * HTTP interfaces resolve under base (meshInterfaceURL), gRPC ones go, signatures no longer match.
127
+ * Streaming stays as declared, this transport streams. Throws when no interface remains.
128
+ */
129
+ export declare function rewriteAgentCard(card: unknown, base: string): Record<string, unknown>;
130
+ /**
131
+ * The mesh URL of one agent interface, as agentmesh-node resolves it. The
132
+ * provider's node names an interface it can reach by a reference relative to
133
+ * the service root ("invoke", "a2a/rest", "" for the root), which resolves
134
+ * under base; anything else maps to base itself. A reference that could leave
135
+ * the service (a dot segment, a rooted path, a scheme or a host) also maps to
136
+ * base: a provider must not be able to point the caller at another path on
137
+ * this node. Mirrors Go's url.Parse and URL.EscapedPath for the parts it reads.
138
+ */
139
+ export declare function meshInterfaceURL(base: string, raw: string): string;
140
+ export interface HTTPStreamOptions {
141
+ /** Bounds the whole exchange; without one, the response headers must arrive within a minute and the body is unbounded. */
142
+ signal?: AbortSignal;
143
+ }
144
+ /**
145
+ * Client side of /libp2p-http, as go-libp2p-http's RoundTripper: one stream
146
+ * per request, plain HTTP/1.1 with Host set to the peer ID and the biscuit in
147
+ * X-Mesh-Biscuit. Resolves once the response headers are in; the body streams
148
+ * after, so an SSE response is consumed as the peer sends it. An agent card
149
+ * is served rewritten for the mesh (rewriteAgentCard), as agentmesh-node serves one.
150
+ */
151
+ export declare function fetchOverStream(conn: Connection, biscuit: Uint8Array, request: Request, options?: HTTPStreamOptions): Promise<Response>;
152
+ export interface HTTPRequestOptions {
153
+ method?: string;
154
+ headers?: Record<string, string>;
155
+ body?: Uint8Array | string;
156
+ signal?: AbortSignal;
157
+ }
158
+ export interface HTTPResponse {
159
+ status: number;
160
+ headers: Record<string, string>;
161
+ body: Uint8Array;
162
+ text(): string;
163
+ }
164
+ /** One request to /<type>/<name>/<path> on a peer, body read whole. */
165
+ export declare function httpRequestOverStream(conn: Connection, biscuit: Uint8Array, targetService: string, path: string, options?: HTTPRequestOptions): Promise<HTTPResponse>;