@agentidcard/sdk 1.0.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/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @agentidcard/sdk
2
+
3
+ Official JavaScript SDK for Agent ID Card.
4
+
5
+ Works in Node.js 18+ and modern browsers (uses WebCrypto API).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @agentidcard/sdk
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```js
16
+ import { AilClient, verifyOffline } from "@agentidcard/sdk";
17
+
18
+ const client = new AilClient({ serverUrl: "https://api.agentidcard.org" });
19
+
20
+ // 1. Register owner
21
+ const owner = await client.registerOwner({ email: "you@example.com", org: "your_org" });
22
+
23
+ // 2. Verify email with the OTP sent to the owner's inbox
24
+ await client.verifyEmail({ owner_key_id: owner.owner_key_id, otp: "123456" });
25
+
26
+ // 3. Register agent (SDK handles signing automatically)
27
+ const agent = await client.registerAgent({
28
+ owner_key_id: owner.owner_key_id,
29
+ private_key_jwk: owner.private_key_jwk,
30
+ payload: {
31
+ display_name: "MyAgent",
32
+ role: "assistant",
33
+ scope: {
34
+ network: "none",
35
+ secrets: "none",
36
+ write_access: false,
37
+ approval_policy: {
38
+ irreversible_actions: "human_required",
39
+ external_posting: "human_required",
40
+ destructive_file_ops: "human_required",
41
+ },
42
+ },
43
+ },
44
+ });
45
+
46
+ console.log(agent.ail_id); // AIL-2026-00001
47
+
48
+ // 4. Verify credential (online)
49
+ const result = await client.verify(agent.credential.token);
50
+ console.log(result.valid, result.display_name);
51
+
52
+ // 5. Verify offline (no server call)
53
+ const keys = await client.getPublicKeys();
54
+ const offline = await verifyOffline(agent.credential.token, keys.keys[0]);
55
+ ```
56
+
57
+ ## Session-based registration
58
+
59
+ ```js
60
+ const login = await client.loginOwner({ email: "you@example.com" });
61
+
62
+ const session = await client.verifyLogin({
63
+ owner_key_id: login.owner_key_id,
64
+ otp: "123456",
65
+ });
66
+
67
+ const agent = await client.registerAgentWithSession({
68
+ session_token: session.session_token,
69
+ payload: {
70
+ display_name: "OpsAgent",
71
+ role: "automation",
72
+ provider: "openai",
73
+ model: "gpt-5.4",
74
+ scope: {
75
+ network: "restricted",
76
+ secrets: "none",
77
+ write_access: false,
78
+ approval_policy: {
79
+ irreversible_actions: "human_required",
80
+ external_posting: "human_required",
81
+ destructive_file_ops: "human_required",
82
+ },
83
+ },
84
+ },
85
+ });
86
+ ```
87
+
88
+ ## Build a v1 envelope
89
+
90
+ ```js
91
+ import { buildEnvelope } from "@agentidcard/sdk";
92
+
93
+ const envelope = buildEnvelope({
94
+ ail_id: agent.ail_id,
95
+ credential: agent.credential,
96
+ signal_glyph: agent.signal_glyph,
97
+ behavior_fingerprint: agent.behavior_fingerprint,
98
+ agent: {
99
+ id: "agent_myagent_01",
100
+ provider: "anthropic",
101
+ model: "claude-sonnet-4-6",
102
+ runtime: "claude_code",
103
+ },
104
+ owner: {
105
+ key_id: owner.owner_key_id,
106
+ org: "your_org",
107
+ email_hash: "sha256:...",
108
+ },
109
+ scope: agent_scope,
110
+ delegation: { mode: "direct", chain_depth: 0 },
111
+ runtime: {
112
+ session_id: "sess_001",
113
+ run_id: "run_001",
114
+ surface: "cli",
115
+ host: "localhost",
116
+ },
117
+ });
118
+ ```
119
+
120
+ ## Revoke an agent
121
+
122
+ ```js
123
+ await client.revokeAgent({
124
+ ail_id: agent.ail_id,
125
+ owner_key_id: owner.owner_key_id,
126
+ private_key_jwk: owner.private_key_jwk,
127
+ });
128
+ ```
129
+
130
+ ## API
131
+
132
+ ### `new AilClient({ serverUrl })`
133
+
134
+ ### `client.registerOwner({ email, org? })`
135
+ ### `client.verifyEmail({ owner_key_id, otp })`
136
+ ### `client.loginOwner({ email })`
137
+ ### `client.verifyLogin({ owner_key_id, otp })`
138
+ ### `client.registerAgent({ owner_key_id, private_key_jwk, payload })`
139
+ ### `client.registerAgentWithSession({ session_token, payload })`
140
+ ### `client.revokeAgent({ ail_id, owner_key_id, private_key_jwk })`
141
+ ### `client.verify(token)` - online verification
142
+ ### `client.verifyOffline(token)` - offline (fetches JWKS once, caches)
143
+ ### `client.getPublicKeys()` - returns JWKS
144
+
145
+ ### `verifyOffline(token, publicKeyJwk)` - standalone offline verification
146
+
147
+ ### `buildEnvelope(options)` - assemble a v1 envelope
148
+
149
+ ### `generateOwnerKeypair()` - generate EC P-256 keypair (returns `{ public_key_jwk, private_key_jwk }`)
150
+ ### `signPayload(payload, private_key_jwk)` - sign a payload, returns base64url
151
+ ### `canonicalJson(obj)` - canonical JSON serialization
package/index.d.ts ADDED
@@ -0,0 +1,122 @@
1
+ export type Jwk = Record<string, unknown>;
2
+ export type JsonValue =
3
+ | string
4
+ | number
5
+ | boolean
6
+ | null
7
+ | JsonValue[]
8
+ | { [key: string]: JsonValue };
9
+
10
+ export interface OwnerRegistrationResponse {
11
+ owner_key_id: string;
12
+ public_key_jwk: Jwk;
13
+ private_key_jwk: Jwk;
14
+ message?: string;
15
+ }
16
+
17
+ export interface LoginResponse {
18
+ owner_key_id: string;
19
+ message?: string;
20
+ }
21
+
22
+ export interface VerifyLoginResponse {
23
+ authenticated: boolean;
24
+ session_token: string;
25
+ owner: Record<string, unknown>;
26
+ agents: Array<Record<string, unknown>>;
27
+ }
28
+
29
+ export interface AgentRegistrationOptions {
30
+ display_name: string;
31
+ role: string;
32
+ provider?: string | null;
33
+ model?: string | null;
34
+ scope: Record<string, unknown>;
35
+ wallet_address?: string;
36
+ plan?: string;
37
+ }
38
+
39
+ export interface CredentialResponse {
40
+ ail_id: string;
41
+ credential: Record<string, unknown>;
42
+ signal_glyph: Record<string, unknown>;
43
+ behavior_fingerprint: Record<string, unknown>;
44
+ nft_image_url?: string;
45
+ nft_metadata_url?: string;
46
+ nft?: {
47
+ token_id: string;
48
+ tx_hash?: string;
49
+ };
50
+ }
51
+
52
+ export interface OfflineVerificationResult {
53
+ valid: boolean;
54
+ ail_id?: string;
55
+ display_name?: string;
56
+ role?: string;
57
+ owner_org?: string;
58
+ issued?: string;
59
+ expires?: string;
60
+ reason?: string;
61
+ }
62
+
63
+ export interface AilClientOptions {
64
+ serverUrl?: string;
65
+ }
66
+
67
+ export interface ReputationQueryParams {
68
+ source?: string;
69
+ season?: number;
70
+ limit?: number;
71
+ dimension?: string;
72
+ }
73
+
74
+ export declare class AilClient {
75
+ constructor(options?: AilClientOptions);
76
+ registerOwner(options: { email: string; org?: string }): Promise<OwnerRegistrationResponse>;
77
+ verifyEmail(options: { owner_key_id: string; otp: string }): Promise<Record<string, unknown>>;
78
+ loginOwner(options: { email: string }): Promise<LoginResponse>;
79
+ verifyLogin(options: { owner_key_id: string; otp: string }): Promise<VerifyLoginResponse>;
80
+ registerAgent(options: {
81
+ owner_key_id: string;
82
+ private_key_jwk: Jwk;
83
+ payload: AgentRegistrationOptions;
84
+ }): Promise<CredentialResponse>;
85
+ registerAgentWithSession(options: {
86
+ session_token: string;
87
+ payload: AgentRegistrationOptions;
88
+ }): Promise<CredentialResponse>;
89
+ revokeAgent(options: {
90
+ ail_id: string;
91
+ owner_key_id: string;
92
+ private_key_jwk: Jwk;
93
+ }): Promise<Record<string, unknown>>;
94
+ verify(token: string): Promise<Record<string, unknown>>;
95
+ verifyOffline(token: string): Promise<OfflineVerificationResult>;
96
+ getPublicKeys(): Promise<Record<string, unknown>>;
97
+ getReputation(ailId: string): Promise<Record<string, unknown>>;
98
+ getReputationHistory(ailId: string, params?: ReputationQueryParams): Promise<Record<string, unknown>>;
99
+ compareAgents(ailId: string, otherAilId: string): Promise<Record<string, unknown>>;
100
+ getLeaderboard(params?: ReputationQueryParams): Promise<Record<string, unknown>>;
101
+ getBadges(ailId: string): Promise<Record<string, unknown>>;
102
+ getSeasonReport(ailId: string, season: number, params?: { source?: string }): Promise<Record<string, unknown>>;
103
+ awardBadge(options: {
104
+ source_name: string;
105
+ agent_id: string;
106
+ badge_id: string;
107
+ private_key_jwk: Jwk;
108
+ merkle_proof?: string | null;
109
+ }): Promise<Record<string, unknown>>;
110
+ }
111
+
112
+ export declare function verifyOffline(token: string, publicKeyJwk: Jwk): Promise<OfflineVerificationResult>;
113
+ export declare function buildEnvelope(options: Record<string, unknown>): Record<string, unknown>;
114
+ export declare function generateOwnerKeypair(): Promise<{ public_key_jwk: Jwk; private_key_jwk: Jwk }>;
115
+ export declare function signPayload(payload: JsonValue | Record<string, unknown>, privateKeyJwk: Jwk): Promise<string>;
116
+ export declare function verifyOwnerSignature(
117
+ payload: JsonValue | Record<string, unknown>,
118
+ signatureB64url: string,
119
+ publicKeyJwk: Jwk
120
+ ): Promise<boolean>;
121
+ export declare function canonicalJson(obj: JsonValue | Record<string, unknown>): string;
122
+ export declare function sha256hexAsync(data: string): Promise<string>;
package/index.mjs ADDED
@@ -0,0 +1,9 @@
1
+ export { AilClient, verifyOffline } from "./src/client.mjs";
2
+ export { buildEnvelope } from "./src/envelope.mjs";
3
+ export {
4
+ generateOwnerKeypair,
5
+ signPayload,
6
+ verifyOwnerSignature,
7
+ canonicalJson,
8
+ sha256hexAsync,
9
+ } from "./src/crypto.mjs";
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@agentidcard/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Official JavaScript SDK for Agent ID Card - AI agent identity credentials",
5
+ "type": "module",
6
+ "main": "./index.mjs",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.mjs"
12
+ }
13
+ },
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ },
17
+ "files": [
18
+ "index.d.ts",
19
+ "index.mjs",
20
+ "src/",
21
+ "README.md"
22
+ ],
23
+ "keywords": ["agent-identity", "agentidcard", "ail", "credential", "jwt", "nft", "erc721"],
24
+ "license": "MIT",
25
+ "author": "Agent ID Card <contact@agentidcard.org>",
26
+ "homepage": "https://agentidcard.org",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/sinmb79/Agent-Identity-Layer.git",
30
+ "directory": "sdk/js"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "jose": "^5.2.3"
37
+ }
38
+ }
package/src/client.mjs ADDED
@@ -0,0 +1,211 @@
1
+ import { signPayload } from "./crypto.mjs";
2
+ import { jwtVerify, importJWK } from "jose";
3
+
4
+ /**
5
+ * AilClient communicates with the Agent ID Card issuance server.
6
+ *
7
+ * Usage:
8
+ * const client = new AilClient({ serverUrl: "https://api.agentidcard.org" });
9
+ */
10
+ export class AilClient {
11
+ constructor({ serverUrl = "https://api.agentidcard.org" } = {}) {
12
+ this.serverUrl = serverUrl.replace(/\/$/, "");
13
+ }
14
+
15
+ #withQuery(path, params = {}) {
16
+ const search = new URLSearchParams();
17
+ for (const [key, value] of Object.entries(params)) {
18
+ if (value === undefined || value === null || value === "") continue;
19
+ search.set(key, String(value));
20
+ }
21
+ const query = search.toString();
22
+ return query ? `${path}?${query}` : path;
23
+ }
24
+
25
+ async #post(path, body) {
26
+ const res = await fetch(`${this.serverUrl}${path}`, {
27
+ method: "POST",
28
+ headers: { "Content-Type": "application/json" },
29
+ body: JSON.stringify(body),
30
+ });
31
+ const data = await res.json();
32
+ if (!res.ok) {
33
+ const err = new Error(data.message ?? data.error ?? `HTTP ${res.status}`);
34
+ err.code = data.error;
35
+ err.status = res.status;
36
+ throw err;
37
+ }
38
+ return data;
39
+ }
40
+
41
+ async #get(path) {
42
+ const res = await fetch(`${this.serverUrl}${path}`);
43
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
44
+ return res.json();
45
+ }
46
+
47
+ // -------------------------------------------------------------------------
48
+ // Owner registration and login
49
+ // -------------------------------------------------------------------------
50
+
51
+ /**
52
+ * Register a new owner and receive their EC P-256 keypair.
53
+ * Store private_key_jwk securely - it is not kept by the server.
54
+ */
55
+ async registerOwner({ email, org }) {
56
+ return this.#post("/owners/register", { email, org });
57
+ }
58
+
59
+ /**
60
+ * Verify owner email with the OTP received after registration.
61
+ */
62
+ async verifyEmail({ owner_key_id, otp }) {
63
+ return this.#post("/owners/verify-email", { owner_key_id, otp });
64
+ }
65
+
66
+ /**
67
+ * Request a login OTP for an existing verified owner.
68
+ */
69
+ async loginOwner({ email }) {
70
+ return this.#post("/owners/login", { email });
71
+ }
72
+
73
+ /**
74
+ * Verify a login OTP and receive a session token for session-based registration.
75
+ */
76
+ async verifyLogin({ owner_key_id, otp }) {
77
+ return this.#post("/owners/verify-login", { owner_key_id, otp });
78
+ }
79
+
80
+ // -------------------------------------------------------------------------
81
+ // Agent registration
82
+ // -------------------------------------------------------------------------
83
+
84
+ /**
85
+ * Register an agent and receive a signed v1 credential.
86
+ *
87
+ * The SDK handles signing the payload with the owner's private key automatically.
88
+ */
89
+ async registerAgent({ owner_key_id, private_key_jwk, payload }) {
90
+ const owner_signature = await signPayload(payload, private_key_jwk);
91
+ return this.#post("/agents/register", { owner_key_id, payload, owner_signature });
92
+ }
93
+
94
+ /**
95
+ * Register an agent using a session token from verifyLogin.
96
+ */
97
+ async registerAgentWithSession({ session_token, payload }) {
98
+ return this.#post("/agents/register-session", { session_token, payload });
99
+ }
100
+
101
+ // -------------------------------------------------------------------------
102
+ // Revocation
103
+ // -------------------------------------------------------------------------
104
+
105
+ /**
106
+ * Revoke an agent credential.
107
+ * The SDK handles signing the revoke payload automatically.
108
+ */
109
+ async revokeAgent({ ail_id, owner_key_id, private_key_jwk }) {
110
+ const revokePayload = { action: "revoke", ail_id };
111
+ const owner_signature = await signPayload(revokePayload, private_key_jwk);
112
+ const res = await fetch(`${this.serverUrl}/agents/${ail_id}/revoke`, {
113
+ method: "DELETE",
114
+ headers: { "Content-Type": "application/json" },
115
+ body: JSON.stringify({ owner_key_id, owner_signature }),
116
+ });
117
+ const data = await res.json();
118
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
119
+ return data;
120
+ }
121
+
122
+ // -------------------------------------------------------------------------
123
+ // Verification
124
+ // -------------------------------------------------------------------------
125
+
126
+ /**
127
+ * Verify a credential online (calls the server).
128
+ */
129
+ async verify(token) {
130
+ return this.#post("/verify", { token });
131
+ }
132
+
133
+ /**
134
+ * Verify a credential offline using the server's public key.
135
+ * Fetches the JWKS once and caches it.
136
+ */
137
+ async verifyOffline(token) {
138
+ if (!this._cachedJwks) {
139
+ this._cachedJwks = await this.#get("/keys");
140
+ }
141
+ return verifyOffline(token, this._cachedJwks.keys[0]);
142
+ }
143
+
144
+ /**
145
+ * Fetch the Agent ID Card JWKS (public keys for offline verification).
146
+ */
147
+ async getPublicKeys() {
148
+ return this.#get("/keys");
149
+ }
150
+
151
+ // -------------------------------------------------------------------------
152
+ // Reputation and achievements
153
+ // -------------------------------------------------------------------------
154
+
155
+ async getReputation(ailId) {
156
+ return this.#get(`/reputation/${encodeURIComponent(ailId)}`);
157
+ }
158
+
159
+ async getReputationHistory(ailId, params = {}) {
160
+ return this.#get(this.#withQuery(`/reputation/${encodeURIComponent(ailId)}/history`, params));
161
+ }
162
+
163
+ async compareAgents(ailId, otherAilId) {
164
+ return this.#get(this.#withQuery(`/reputation/${encodeURIComponent(ailId)}/compare`, { with: otherAilId }));
165
+ }
166
+
167
+ async getLeaderboard(params = {}) {
168
+ return this.#get(this.#withQuery("/reputation/leaderboard", params));
169
+ }
170
+
171
+ async getBadges(ailId) {
172
+ return this.#get(`/reputation/${encodeURIComponent(ailId)}/badges`);
173
+ }
174
+
175
+ async getSeasonReport(ailId, season, params = {}) {
176
+ return this.#get(
177
+ this.#withQuery(`/reputation/${encodeURIComponent(ailId)}/season/${season}`, params)
178
+ );
179
+ }
180
+
181
+ async awardBadge({ source_name, agent_id, badge_id, private_key_jwk, merkle_proof = null }) {
182
+ const payload = { source_name, agent_id, badge_id, merkle_proof };
183
+ const signature = await signPayload(payload, private_key_jwk);
184
+ return this.#post("/reputation/badge", { ...payload, signature });
185
+ }
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // Standalone offline verification (no server required)
190
+ // Provide the public key JWK from GET /keys
191
+ // ---------------------------------------------------------------------------
192
+ export async function verifyOffline(token, publicKeyJwk) {
193
+ try {
194
+ const publicKey = await importJWK(publicKeyJwk, "ES256");
195
+ const { payload } = await jwtVerify(token, publicKey, {
196
+ issuer: ["agentidcard.org", "22blabs.ai"],
197
+ algorithms: ["ES256"],
198
+ });
199
+ return {
200
+ valid: true,
201
+ ail_id: payload.ail_id,
202
+ display_name: payload.display_name,
203
+ role: payload.role,
204
+ owner_org: payload.owner_org,
205
+ issued: new Date(payload.iat * 1000).toISOString(),
206
+ expires: new Date(payload.exp * 1000).toISOString(),
207
+ };
208
+ } catch (err) {
209
+ return { valid: false, reason: err.message };
210
+ }
211
+ }
package/src/crypto.mjs ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Crypto utilities for the AIL JS SDK.
3
+ * Uses the WebCrypto API (globalThis.crypto.subtle) — works in Node.js 18+
4
+ * and all modern browsers.
5
+ */
6
+
7
+ import { createHash } from "node:crypto";
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Canonical JSON
11
+ // ---------------------------------------------------------------------------
12
+ export function canonicalJson(obj) {
13
+ return JSON.stringify(obj, (key, value) => {
14
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
15
+ return Object.fromEntries(
16
+ Object.entries(value).sort(([a], [b]) => a.localeCompare(b))
17
+ );
18
+ }
19
+ return value;
20
+ });
21
+ }
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // SHA-256 hex
25
+ // ---------------------------------------------------------------------------
26
+ export function sha256hex(data) {
27
+ // Works in Node.js; in browsers, use SubtleCrypto instead
28
+ try {
29
+ return createHash("sha256").update(data).digest("hex");
30
+ } catch {
31
+ // Browser fallback (sync not available — caller should use sha256hexAsync)
32
+ throw new Error("Use sha256hexAsync in browser environments.");
33
+ }
34
+ }
35
+
36
+ export async function sha256hexAsync(data) {
37
+ const buf = new TextEncoder().encode(data);
38
+ const hash = await globalThis.crypto.subtle.digest("SHA-256", buf);
39
+ return Array.from(new Uint8Array(hash))
40
+ .map((b) => b.toString(16).padStart(2, "0"))
41
+ .join("");
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Owner keypair — EC P-256
46
+ // ---------------------------------------------------------------------------
47
+ export async function generateOwnerKeypair() {
48
+ const { privateKey, publicKey } = await globalThis.crypto.subtle.generateKey(
49
+ { name: "ECDSA", namedCurve: "P-256" },
50
+ true,
51
+ ["sign", "verify"]
52
+ );
53
+
54
+ const private_key_jwk = await globalThis.crypto.subtle.exportKey("jwk", privateKey);
55
+ const public_key_jwk = await globalThis.crypto.subtle.exportKey("jwk", publicKey);
56
+
57
+ // owner_key_id is assigned by the server; return a local placeholder
58
+ return { public_key_jwk, private_key_jwk };
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Sign payload (owner signs registration or revoke payload)
63
+ // Returns base64url IEEE P1363 signature (64 bytes for P-256)
64
+ // ---------------------------------------------------------------------------
65
+ export async function signPayload(payload, privateKeyJwk) {
66
+ const key = await globalThis.crypto.subtle.importKey(
67
+ "jwk",
68
+ privateKeyJwk,
69
+ { name: "ECDSA", namedCurve: "P-256" },
70
+ false,
71
+ ["sign"]
72
+ );
73
+ const data = new TextEncoder().encode(canonicalJson(payload));
74
+ const sig = await globalThis.crypto.subtle.sign(
75
+ { name: "ECDSA", hash: "SHA-256" },
76
+ key,
77
+ data
78
+ );
79
+ return Buffer.from(sig).toString("base64url");
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Verify owner signature
84
+ // ---------------------------------------------------------------------------
85
+ export async function verifyOwnerSignature(payload, signatureB64url, publicKeyJwk) {
86
+ try {
87
+ const key = await globalThis.crypto.subtle.importKey(
88
+ "jwk",
89
+ publicKeyJwk,
90
+ { name: "ECDSA", namedCurve: "P-256" },
91
+ false,
92
+ ["verify"]
93
+ );
94
+ const data = new TextEncoder().encode(canonicalJson(payload));
95
+ const sig = Buffer.from(signatureB64url, "base64url");
96
+ return await globalThis.crypto.subtle.verify(
97
+ { name: "ECDSA", hash: "SHA-256" },
98
+ key,
99
+ sig,
100
+ data
101
+ );
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * buildEnvelope — assembles a complete v1 AIL envelope from a registered credential.
3
+ *
4
+ * Usage:
5
+ * const envelope = buildEnvelope({
6
+ * ail_id, credential, signal_glyph, behavior_fingerprint,
7
+ * agent: { id, provider, model, runtime },
8
+ * owner: { key_id, org, email_hash },
9
+ * scope,
10
+ * delegation: { mode, delegated_by, approved_by, chain_depth, task_ref },
11
+ * runtime: { session_id, run_id, surface, host, cwd },
12
+ * })
13
+ */
14
+ export function buildEnvelope({
15
+ ail_id,
16
+ credential,
17
+ signal_glyph,
18
+ behavior_fingerprint,
19
+ agent,
20
+ owner = null,
21
+ scope,
22
+ delegation,
23
+ runtime,
24
+ extensions = {},
25
+ }) {
26
+ const now = new Date().toISOString();
27
+
28
+ return {
29
+ version: "ail.v1",
30
+ ail_id: ail_id ?? null,
31
+
32
+ credential: credential ?? null,
33
+
34
+ agent: {
35
+ id: agent.id,
36
+ display_name: agent.display_name ?? credential?.display_name,
37
+ role: agent.role ?? credential?.role,
38
+ provider: agent.provider ?? null,
39
+ model: agent.model ?? null,
40
+ runtime: agent.runtime ?? null,
41
+ },
42
+
43
+ owner: owner
44
+ ? {
45
+ key_id: owner.key_id,
46
+ org: owner.org ?? null,
47
+ email_hash: owner.email_hash ?? null,
48
+ }
49
+ : null,
50
+
51
+ scope: scope ?? {
52
+ workspace: [],
53
+ repos: [],
54
+ network: "restricted",
55
+ secrets: "none",
56
+ write_access: false,
57
+ approval_policy: {
58
+ irreversible_actions: "human_required",
59
+ external_posting: "human_required",
60
+ destructive_file_ops: "human_required",
61
+ },
62
+ },
63
+
64
+ signal_glyph: signal_glyph ?? null,
65
+ behavior_fingerprint: behavior_fingerprint ?? null,
66
+
67
+ delegation: {
68
+ mode: delegation?.mode ?? "direct",
69
+ delegated_by: delegation?.delegated_by ?? null,
70
+ approved_by: delegation?.approved_by ?? null,
71
+ chain_depth: delegation?.chain_depth ?? 0,
72
+ task_ref: delegation?.task_ref ?? null,
73
+ },
74
+
75
+ runtime: {
76
+ session_id: runtime?.session_id ?? null,
77
+ run_id: runtime?.run_id ?? null,
78
+ surface: runtime?.surface ?? null,
79
+ host: runtime?.host ?? null,
80
+ cwd: runtime?.cwd ?? null,
81
+ time: runtime?.time ?? now,
82
+ },
83
+
84
+ verification: ail_id && credential
85
+ ? {
86
+ strength: "cryptographically_signed",
87
+ issuer: credential.issuer,
88
+ issuer_key_id: credential.issuer_key_id,
89
+ token_type: "JWT",
90
+ signed: true,
91
+ verify_url: `${getIssuerBase(credential.issuer)}/verify`,
92
+ evidence: ["22blabs_registry", "owner_key_delegation", "jwt_signature"],
93
+ attestation_ref: `ail_id:${ail_id}`,
94
+ }
95
+ : {
96
+ strength: "local_runtime_asserted",
97
+ signed: false,
98
+ evidence: ["runtime_session_binding"],
99
+ attestation_ref: null,
100
+ },
101
+
102
+ extensions,
103
+ };
104
+ }
105
+
106
+ function getIssuerBase(issuer) {
107
+ if (!issuer.startsWith("http")) return `https://${issuer}/api`;
108
+ return issuer;
109
+ }