@agentcred-ai/api 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AgentCred Contributors
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/dist/index.cjs ADDED
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => index_default
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_hono = require("hono");
27
+ var import_cors = require("hono/cors");
28
+ var import_zod = require("zod");
29
+ var import_jose = require("jose");
30
+ var JwkSchema = import_zod.z.object({
31
+ kty: import_zod.z.literal("OKP"),
32
+ crv: import_zod.z.literal("Ed25519"),
33
+ x: import_zod.z.string(),
34
+ use: import_zod.z.literal("sig").optional(),
35
+ alg: import_zod.z.literal("EdDSA").optional()
36
+ });
37
+ var RegisterKeySchema = import_zod.z.object({
38
+ public_key: JwkSchema
39
+ });
40
+ var EnvelopeSchema = import_zod.z.object({
41
+ agentcred: import_zod.z.object({
42
+ v: import_zod.z.string(),
43
+ jws: import_zod.z.string(),
44
+ github: import_zod.z.string(),
45
+ agent: import_zod.z.string()
46
+ }),
47
+ content: import_zod.z.string()
48
+ });
49
+ async function checkRateLimit(kv, ip, endpoint, limit = 60) {
50
+ const minute = Math.floor(Date.now() / 6e4);
51
+ const key = `ratelimit:${ip}:${endpoint}:${minute}`;
52
+ const current = await kv.get(key);
53
+ const count = current ? parseInt(current, 10) : 0;
54
+ if (count >= limit) {
55
+ return false;
56
+ }
57
+ await kv.put(key, String(count + 1), { expirationTtl: 60 });
58
+ return true;
59
+ }
60
+ async function sha256Hex(content) {
61
+ const encoder = new TextEncoder();
62
+ const data = encoder.encode(content);
63
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
64
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
65
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
66
+ }
67
+ async function verifyGitHubToken(token) {
68
+ try {
69
+ const res = await fetch("https://api.github.com/user", {
70
+ headers: {
71
+ Authorization: `Bearer ${token}`,
72
+ Accept: "application/vnd.github+json",
73
+ "User-Agent": "AgentCred-API/1.0"
74
+ }
75
+ });
76
+ if (!res.ok) return null;
77
+ const data = await res.json();
78
+ return { login: data.login, id: data.id };
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ var app = new import_hono.Hono();
84
+ app.use(
85
+ "*",
86
+ (0, import_cors.cors)({
87
+ origin: "*",
88
+ allowMethods: ["GET", "POST"],
89
+ allowHeaders: ["Content-Type", "Authorization"]
90
+ })
91
+ );
92
+ app.post("/v1/keys", async (c) => {
93
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
94
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "POST:/v1/keys", 10);
95
+ if (!allowed) {
96
+ return c.json({ error: "Rate limit exceeded" }, 429);
97
+ }
98
+ const authHeader = c.req.header("Authorization");
99
+ if (!authHeader?.startsWith("Bearer ")) {
100
+ return c.json({ error: "Missing or invalid Authorization header" }, 401);
101
+ }
102
+ const token = authHeader.slice(7);
103
+ const ghUser = await verifyGitHubToken(token);
104
+ if (!ghUser) {
105
+ return c.json({ error: "Invalid GitHub token" }, 401);
106
+ }
107
+ let body;
108
+ try {
109
+ body = await c.req.json();
110
+ } catch {
111
+ return c.json({ error: "Invalid JSON body" }, 400);
112
+ }
113
+ const parsed = RegisterKeySchema.safeParse(body);
114
+ if (!parsed.success) {
115
+ return c.json({ error: "Invalid request body", details: parsed.error.issues }, 400);
116
+ }
117
+ const storedKey = {
118
+ github: ghUser.login,
119
+ github_id: ghUser.id,
120
+ public_key: parsed.data.public_key,
121
+ registered_at: (/* @__PURE__ */ new Date()).toISOString()
122
+ };
123
+ await c.env.KEYS.put(`key:${ghUser.login}`, JSON.stringify(storedKey));
124
+ return c.json(
125
+ {
126
+ github: ghUser.login,
127
+ public_key: parsed.data.public_key,
128
+ registered_at: storedKey.registered_at
129
+ },
130
+ 201
131
+ );
132
+ });
133
+ app.get("/v1/keys/:username", async (c) => {
134
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
135
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "GET:/v1/keys", 120);
136
+ if (!allowed) {
137
+ return c.json({ error: "Rate limit exceeded" }, 429);
138
+ }
139
+ const username = c.req.param("username");
140
+ const stored = await c.env.KEYS.get(`key:${username}`);
141
+ if (!stored) {
142
+ return c.json({ error: "Key not found" }, 404);
143
+ }
144
+ const data = JSON.parse(stored);
145
+ return c.json({
146
+ github: data.github,
147
+ public_key: data.public_key,
148
+ registered_at: data.registered_at
149
+ });
150
+ });
151
+ app.post("/v1/verify", async (c) => {
152
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
153
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "POST:/v1/verify", 60);
154
+ if (!allowed) {
155
+ return c.json({ error: "Rate limit exceeded" }, 429);
156
+ }
157
+ let body;
158
+ try {
159
+ body = await c.req.json();
160
+ } catch {
161
+ return c.json({ error: "Invalid JSON body" }, 400);
162
+ }
163
+ let envelopeData = body;
164
+ if (typeof body === "object" && body !== null && "envelope" in body) {
165
+ envelopeData = body.envelope;
166
+ }
167
+ const parsed = EnvelopeSchema.safeParse(envelopeData);
168
+ if (!parsed.success) {
169
+ return c.json(
170
+ { verified: false, error: "Invalid envelope format", details: parsed.error.issues },
171
+ 400
172
+ );
173
+ }
174
+ const envelope = parsed.data;
175
+ const { jws, github: envelopeGithub } = envelope.agentcred;
176
+ const stored = await c.env.KEYS.get(`key:${envelopeGithub}`);
177
+ if (!stored) {
178
+ return c.json(
179
+ { verified: false, error: `No registered key for user: ${envelopeGithub}` },
180
+ 404
181
+ );
182
+ }
183
+ const storedData = JSON.parse(stored);
184
+ try {
185
+ const publicKey = await (0, import_jose.importJWK)(
186
+ { ...storedData.public_key, kty: "OKP", crv: "Ed25519" },
187
+ "EdDSA"
188
+ );
189
+ const { payload, protectedHeader } = await (0, import_jose.compactVerify)(jws, publicKey);
190
+ if (protectedHeader.alg !== "EdDSA") {
191
+ return c.json({ verified: false, error: "Invalid algorithm" }, 400);
192
+ }
193
+ const payloadStr = new TextDecoder().decode(payload);
194
+ const payloadObj = JSON.parse(payloadStr);
195
+ const expectedIss = `${envelopeGithub}@agentcred`;
196
+ if (payloadObj.iss !== expectedIss) {
197
+ return c.json({ verified: false, error: "Issuer mismatch" }, 400);
198
+ }
199
+ const contentHash = await sha256Hex(envelope.content);
200
+ const expectedHash = `sha256:${contentHash}`;
201
+ if (payloadObj.content_hash !== expectedHash) {
202
+ return c.json({ verified: false, error: "Content hash mismatch - content was tampered" }, 400);
203
+ }
204
+ const now = Math.floor(Date.now() / 1e3);
205
+ const maxAge = 24 * 60 * 60;
206
+ if (Math.abs(now - payloadObj.iat) > maxAge) {
207
+ return c.json({ verified: false, error: "Signature expired or timestamp out of range" }, 400);
208
+ }
209
+ return c.json({
210
+ verified: true,
211
+ github: {
212
+ username: storedData.github,
213
+ id: storedData.github_id
214
+ },
215
+ agent: envelope.agentcred.agent,
216
+ signed_at: new Date(payloadObj.iat * 1e3).toISOString()
217
+ });
218
+ } catch (err) {
219
+ const message = err instanceof Error ? err.message : "Unknown error";
220
+ return c.json({ verified: false, error: `Verification failed: ${message}` }, 400);
221
+ }
222
+ });
223
+ app.get("/v1/health", (c) => {
224
+ return c.json({ status: "ok", version: "1.0.0" });
225
+ });
226
+ var index_default = app;
227
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport { z } from 'zod'\nimport { compactVerify, importJWK } from 'jose'\n\nexport type Env = {\n Bindings: {\n KEYS: KVNamespace\n }\n}\n\nexport interface StoredKey {\n github: string\n github_id: number\n public_key: JsonWebKey\n registered_at: string\n}\n\nconst JwkSchema = z.object({\n kty: z.literal('OKP'),\n crv: z.literal('Ed25519'),\n x: z.string(),\n use: z.literal('sig').optional(),\n alg: z.literal('EdDSA').optional(),\n})\n\nconst RegisterKeySchema = z.object({\n public_key: JwkSchema,\n})\n\nconst EnvelopeSchema = z.object({\n agentcred: z.object({\n v: z.string(),\n jws: z.string(),\n github: z.string(),\n agent: z.string(),\n }),\n content: z.string(),\n})\n\nasync function checkRateLimit(\n kv: KVNamespace,\n ip: string,\n endpoint: string,\n limit: number = 60\n): Promise<boolean> {\n const minute = Math.floor(Date.now() / 60000)\n const key = `ratelimit:${ip}:${endpoint}:${minute}`\n const current = await kv.get(key)\n const count = current ? parseInt(current, 10) : 0\n\n if (count >= limit) {\n return false\n }\n\n await kv.put(key, String(count + 1), { expirationTtl: 60 })\n return true\n}\n\nasync function sha256Hex(content: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(content)\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nasync function verifyGitHubToken(\n token: string\n): Promise<{ login: string; id: number } | null> {\n try {\n const res = await fetch('https://api.github.com/user', {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'AgentCred-API/1.0',\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as { login: string; id: number }\n return { login: data.login, id: data.id }\n } catch {\n return null\n }\n}\n\nconst app = new Hono<Env>()\n\napp.use(\n '*',\n cors({\n origin: '*',\n allowMethods: ['GET', 'POST'],\n allowHeaders: ['Content-Type', 'Authorization'],\n })\n)\n\napp.post('/v1/keys', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'POST:/v1/keys', 10)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n const authHeader = c.req.header('Authorization')\n if (!authHeader?.startsWith('Bearer ')) {\n return c.json({ error: 'Missing or invalid Authorization header' }, 401)\n }\n const token = authHeader.slice(7)\n\n const ghUser = await verifyGitHubToken(token)\n if (!ghUser) {\n return c.json({ error: 'Invalid GitHub token' }, 401)\n }\n\n let body: unknown\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400)\n }\n\n const parsed = RegisterKeySchema.safeParse(body)\n if (!parsed.success) {\n return c.json({ error: 'Invalid request body', details: parsed.error.issues }, 400)\n }\n\n const storedKey: StoredKey = {\n github: ghUser.login,\n github_id: ghUser.id,\n public_key: parsed.data.public_key as JsonWebKey,\n registered_at: new Date().toISOString(),\n }\n\n await c.env.KEYS.put(`key:${ghUser.login}`, JSON.stringify(storedKey))\n\n return c.json(\n {\n github: ghUser.login,\n public_key: parsed.data.public_key,\n registered_at: storedKey.registered_at,\n },\n 201\n )\n})\n\napp.get('/v1/keys/:username', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'GET:/v1/keys', 120)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n const username = c.req.param('username')\n const stored = await c.env.KEYS.get(`key:${username}`)\n\n if (!stored) {\n return c.json({ error: 'Key not found' }, 404)\n }\n\n const data: StoredKey = JSON.parse(stored)\n return c.json({\n github: data.github,\n public_key: data.public_key,\n registered_at: data.registered_at,\n })\n})\n\napp.post('/v1/verify', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'POST:/v1/verify', 60)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n let body: unknown\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400)\n }\n\n let envelopeData: unknown = body\n if (\n typeof body === 'object' &&\n body !== null &&\n 'envelope' in body\n ) {\n envelopeData = (body as Record<string, unknown>).envelope\n }\n\n const parsed = EnvelopeSchema.safeParse(envelopeData)\n if (!parsed.success) {\n return c.json(\n { verified: false, error: 'Invalid envelope format', details: parsed.error.issues },\n 400\n )\n }\n\n const envelope = parsed.data\n const { jws, github: envelopeGithub } = envelope.agentcred\n\n const stored = await c.env.KEYS.get(`key:${envelopeGithub}`)\n if (!stored) {\n return c.json(\n { verified: false, error: `No registered key for user: ${envelopeGithub}` },\n 404\n )\n }\n\n const storedData: StoredKey = JSON.parse(stored)\n\n try {\n const publicKey = await importJWK(\n { ...storedData.public_key, kty: 'OKP', crv: 'Ed25519' },\n 'EdDSA'\n )\n\n const { payload, protectedHeader } = await compactVerify(jws, publicKey)\n\n if (protectedHeader.alg !== 'EdDSA') {\n return c.json({ verified: false, error: 'Invalid algorithm' }, 400)\n }\n\n const payloadStr = new TextDecoder().decode(payload)\n const payloadObj = JSON.parse(payloadStr) as {\n iss: string\n sub: string\n iat: number\n content_hash: string\n content_type?: string\n nonce?: string\n }\n\n const expectedIss = `${envelopeGithub}@agentcred`\n if (payloadObj.iss !== expectedIss) {\n return c.json({ verified: false, error: 'Issuer mismatch' }, 400)\n }\n\n const contentHash = await sha256Hex(envelope.content)\n const expectedHash = `sha256:${contentHash}`\n if (payloadObj.content_hash !== expectedHash) {\n return c.json({ verified: false, error: 'Content hash mismatch - content was tampered' }, 400)\n }\n\n // ±24 hours window per spec section 6.2\n const now = Math.floor(Date.now() / 1000)\n const maxAge = 24 * 60 * 60\n if (Math.abs(now - payloadObj.iat) > maxAge) {\n return c.json({ verified: false, error: 'Signature expired or timestamp out of range' }, 400)\n }\n\n return c.json({\n verified: true,\n github: {\n username: storedData.github,\n id: storedData.github_id,\n },\n agent: envelope.agentcred.agent,\n signed_at: new Date(payloadObj.iat * 1000).toISOString(),\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error'\n return c.json({ verified: false, error: `Verification failed: ${message}` }, 400)\n }\n})\n\napp.get('/v1/health', (c) => {\n return c.json({ status: 'ok', version: '1.0.0' })\n})\n\nexport default app\n"],"mappings":"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","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+
4
+ type Env = {
5
+ Bindings: {
6
+ KEYS: KVNamespace;
7
+ };
8
+ };
9
+ interface StoredKey {
10
+ github: string;
11
+ github_id: number;
12
+ public_key: JsonWebKey;
13
+ registered_at: string;
14
+ }
15
+ declare const app: Hono<Env, hono_types.BlankSchema, "/">;
16
+
17
+ export { type Env, type StoredKey, app as default };
@@ -0,0 +1,17 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+
4
+ type Env = {
5
+ Bindings: {
6
+ KEYS: KVNamespace;
7
+ };
8
+ };
9
+ interface StoredKey {
10
+ github: string;
11
+ github_id: number;
12
+ public_key: JsonWebKey;
13
+ registered_at: string;
14
+ }
15
+ declare const app: Hono<Env, hono_types.BlankSchema, "/">;
16
+
17
+ export { type Env, type StoredKey, app as default };
package/dist/index.js ADDED
@@ -0,0 +1,206 @@
1
+ // src/index.ts
2
+ import { Hono } from "hono";
3
+ import { cors } from "hono/cors";
4
+ import { z } from "zod";
5
+ import { compactVerify, importJWK } from "jose";
6
+ var JwkSchema = z.object({
7
+ kty: z.literal("OKP"),
8
+ crv: z.literal("Ed25519"),
9
+ x: z.string(),
10
+ use: z.literal("sig").optional(),
11
+ alg: z.literal("EdDSA").optional()
12
+ });
13
+ var RegisterKeySchema = z.object({
14
+ public_key: JwkSchema
15
+ });
16
+ var EnvelopeSchema = z.object({
17
+ agentcred: z.object({
18
+ v: z.string(),
19
+ jws: z.string(),
20
+ github: z.string(),
21
+ agent: z.string()
22
+ }),
23
+ content: z.string()
24
+ });
25
+ async function checkRateLimit(kv, ip, endpoint, limit = 60) {
26
+ const minute = Math.floor(Date.now() / 6e4);
27
+ const key = `ratelimit:${ip}:${endpoint}:${minute}`;
28
+ const current = await kv.get(key);
29
+ const count = current ? parseInt(current, 10) : 0;
30
+ if (count >= limit) {
31
+ return false;
32
+ }
33
+ await kv.put(key, String(count + 1), { expirationTtl: 60 });
34
+ return true;
35
+ }
36
+ async function sha256Hex(content) {
37
+ const encoder = new TextEncoder();
38
+ const data = encoder.encode(content);
39
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
40
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
41
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
42
+ }
43
+ async function verifyGitHubToken(token) {
44
+ try {
45
+ const res = await fetch("https://api.github.com/user", {
46
+ headers: {
47
+ Authorization: `Bearer ${token}`,
48
+ Accept: "application/vnd.github+json",
49
+ "User-Agent": "AgentCred-API/1.0"
50
+ }
51
+ });
52
+ if (!res.ok) return null;
53
+ const data = await res.json();
54
+ return { login: data.login, id: data.id };
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ var app = new Hono();
60
+ app.use(
61
+ "*",
62
+ cors({
63
+ origin: "*",
64
+ allowMethods: ["GET", "POST"],
65
+ allowHeaders: ["Content-Type", "Authorization"]
66
+ })
67
+ );
68
+ app.post("/v1/keys", async (c) => {
69
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
70
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "POST:/v1/keys", 10);
71
+ if (!allowed) {
72
+ return c.json({ error: "Rate limit exceeded" }, 429);
73
+ }
74
+ const authHeader = c.req.header("Authorization");
75
+ if (!authHeader?.startsWith("Bearer ")) {
76
+ return c.json({ error: "Missing or invalid Authorization header" }, 401);
77
+ }
78
+ const token = authHeader.slice(7);
79
+ const ghUser = await verifyGitHubToken(token);
80
+ if (!ghUser) {
81
+ return c.json({ error: "Invalid GitHub token" }, 401);
82
+ }
83
+ let body;
84
+ try {
85
+ body = await c.req.json();
86
+ } catch {
87
+ return c.json({ error: "Invalid JSON body" }, 400);
88
+ }
89
+ const parsed = RegisterKeySchema.safeParse(body);
90
+ if (!parsed.success) {
91
+ return c.json({ error: "Invalid request body", details: parsed.error.issues }, 400);
92
+ }
93
+ const storedKey = {
94
+ github: ghUser.login,
95
+ github_id: ghUser.id,
96
+ public_key: parsed.data.public_key,
97
+ registered_at: (/* @__PURE__ */ new Date()).toISOString()
98
+ };
99
+ await c.env.KEYS.put(`key:${ghUser.login}`, JSON.stringify(storedKey));
100
+ return c.json(
101
+ {
102
+ github: ghUser.login,
103
+ public_key: parsed.data.public_key,
104
+ registered_at: storedKey.registered_at
105
+ },
106
+ 201
107
+ );
108
+ });
109
+ app.get("/v1/keys/:username", async (c) => {
110
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
111
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "GET:/v1/keys", 120);
112
+ if (!allowed) {
113
+ return c.json({ error: "Rate limit exceeded" }, 429);
114
+ }
115
+ const username = c.req.param("username");
116
+ const stored = await c.env.KEYS.get(`key:${username}`);
117
+ if (!stored) {
118
+ return c.json({ error: "Key not found" }, 404);
119
+ }
120
+ const data = JSON.parse(stored);
121
+ return c.json({
122
+ github: data.github,
123
+ public_key: data.public_key,
124
+ registered_at: data.registered_at
125
+ });
126
+ });
127
+ app.post("/v1/verify", async (c) => {
128
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
129
+ const allowed = await checkRateLimit(c.env.KEYS, ip, "POST:/v1/verify", 60);
130
+ if (!allowed) {
131
+ return c.json({ error: "Rate limit exceeded" }, 429);
132
+ }
133
+ let body;
134
+ try {
135
+ body = await c.req.json();
136
+ } catch {
137
+ return c.json({ error: "Invalid JSON body" }, 400);
138
+ }
139
+ let envelopeData = body;
140
+ if (typeof body === "object" && body !== null && "envelope" in body) {
141
+ envelopeData = body.envelope;
142
+ }
143
+ const parsed = EnvelopeSchema.safeParse(envelopeData);
144
+ if (!parsed.success) {
145
+ return c.json(
146
+ { verified: false, error: "Invalid envelope format", details: parsed.error.issues },
147
+ 400
148
+ );
149
+ }
150
+ const envelope = parsed.data;
151
+ const { jws, github: envelopeGithub } = envelope.agentcred;
152
+ const stored = await c.env.KEYS.get(`key:${envelopeGithub}`);
153
+ if (!stored) {
154
+ return c.json(
155
+ { verified: false, error: `No registered key for user: ${envelopeGithub}` },
156
+ 404
157
+ );
158
+ }
159
+ const storedData = JSON.parse(stored);
160
+ try {
161
+ const publicKey = await importJWK(
162
+ { ...storedData.public_key, kty: "OKP", crv: "Ed25519" },
163
+ "EdDSA"
164
+ );
165
+ const { payload, protectedHeader } = await compactVerify(jws, publicKey);
166
+ if (protectedHeader.alg !== "EdDSA") {
167
+ return c.json({ verified: false, error: "Invalid algorithm" }, 400);
168
+ }
169
+ const payloadStr = new TextDecoder().decode(payload);
170
+ const payloadObj = JSON.parse(payloadStr);
171
+ const expectedIss = `${envelopeGithub}@agentcred`;
172
+ if (payloadObj.iss !== expectedIss) {
173
+ return c.json({ verified: false, error: "Issuer mismatch" }, 400);
174
+ }
175
+ const contentHash = await sha256Hex(envelope.content);
176
+ const expectedHash = `sha256:${contentHash}`;
177
+ if (payloadObj.content_hash !== expectedHash) {
178
+ return c.json({ verified: false, error: "Content hash mismatch - content was tampered" }, 400);
179
+ }
180
+ const now = Math.floor(Date.now() / 1e3);
181
+ const maxAge = 24 * 60 * 60;
182
+ if (Math.abs(now - payloadObj.iat) > maxAge) {
183
+ return c.json({ verified: false, error: "Signature expired or timestamp out of range" }, 400);
184
+ }
185
+ return c.json({
186
+ verified: true,
187
+ github: {
188
+ username: storedData.github,
189
+ id: storedData.github_id
190
+ },
191
+ agent: envelope.agentcred.agent,
192
+ signed_at: new Date(payloadObj.iat * 1e3).toISOString()
193
+ });
194
+ } catch (err) {
195
+ const message = err instanceof Error ? err.message : "Unknown error";
196
+ return c.json({ verified: false, error: `Verification failed: ${message}` }, 400);
197
+ }
198
+ });
199
+ app.get("/v1/health", (c) => {
200
+ return c.json({ status: "ok", version: "1.0.0" });
201
+ });
202
+ var index_default = app;
203
+ export {
204
+ index_default as default
205
+ };
206
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport { z } from 'zod'\nimport { compactVerify, importJWK } from 'jose'\n\nexport type Env = {\n Bindings: {\n KEYS: KVNamespace\n }\n}\n\nexport interface StoredKey {\n github: string\n github_id: number\n public_key: JsonWebKey\n registered_at: string\n}\n\nconst JwkSchema = z.object({\n kty: z.literal('OKP'),\n crv: z.literal('Ed25519'),\n x: z.string(),\n use: z.literal('sig').optional(),\n alg: z.literal('EdDSA').optional(),\n})\n\nconst RegisterKeySchema = z.object({\n public_key: JwkSchema,\n})\n\nconst EnvelopeSchema = z.object({\n agentcred: z.object({\n v: z.string(),\n jws: z.string(),\n github: z.string(),\n agent: z.string(),\n }),\n content: z.string(),\n})\n\nasync function checkRateLimit(\n kv: KVNamespace,\n ip: string,\n endpoint: string,\n limit: number = 60\n): Promise<boolean> {\n const minute = Math.floor(Date.now() / 60000)\n const key = `ratelimit:${ip}:${endpoint}:${minute}`\n const current = await kv.get(key)\n const count = current ? parseInt(current, 10) : 0\n\n if (count >= limit) {\n return false\n }\n\n await kv.put(key, String(count + 1), { expirationTtl: 60 })\n return true\n}\n\nasync function sha256Hex(content: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(content)\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nasync function verifyGitHubToken(\n token: string\n): Promise<{ login: string; id: number } | null> {\n try {\n const res = await fetch('https://api.github.com/user', {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'AgentCred-API/1.0',\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as { login: string; id: number }\n return { login: data.login, id: data.id }\n } catch {\n return null\n }\n}\n\nconst app = new Hono<Env>()\n\napp.use(\n '*',\n cors({\n origin: '*',\n allowMethods: ['GET', 'POST'],\n allowHeaders: ['Content-Type', 'Authorization'],\n })\n)\n\napp.post('/v1/keys', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'POST:/v1/keys', 10)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n const authHeader = c.req.header('Authorization')\n if (!authHeader?.startsWith('Bearer ')) {\n return c.json({ error: 'Missing or invalid Authorization header' }, 401)\n }\n const token = authHeader.slice(7)\n\n const ghUser = await verifyGitHubToken(token)\n if (!ghUser) {\n return c.json({ error: 'Invalid GitHub token' }, 401)\n }\n\n let body: unknown\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400)\n }\n\n const parsed = RegisterKeySchema.safeParse(body)\n if (!parsed.success) {\n return c.json({ error: 'Invalid request body', details: parsed.error.issues }, 400)\n }\n\n const storedKey: StoredKey = {\n github: ghUser.login,\n github_id: ghUser.id,\n public_key: parsed.data.public_key as JsonWebKey,\n registered_at: new Date().toISOString(),\n }\n\n await c.env.KEYS.put(`key:${ghUser.login}`, JSON.stringify(storedKey))\n\n return c.json(\n {\n github: ghUser.login,\n public_key: parsed.data.public_key,\n registered_at: storedKey.registered_at,\n },\n 201\n )\n})\n\napp.get('/v1/keys/:username', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'GET:/v1/keys', 120)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n const username = c.req.param('username')\n const stored = await c.env.KEYS.get(`key:${username}`)\n\n if (!stored) {\n return c.json({ error: 'Key not found' }, 404)\n }\n\n const data: StoredKey = JSON.parse(stored)\n return c.json({\n github: data.github,\n public_key: data.public_key,\n registered_at: data.registered_at,\n })\n})\n\napp.post('/v1/verify', async (c) => {\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const allowed = await checkRateLimit(c.env.KEYS, ip, 'POST:/v1/verify', 60)\n if (!allowed) {\n return c.json({ error: 'Rate limit exceeded' }, 429)\n }\n\n let body: unknown\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400)\n }\n\n let envelopeData: unknown = body\n if (\n typeof body === 'object' &&\n body !== null &&\n 'envelope' in body\n ) {\n envelopeData = (body as Record<string, unknown>).envelope\n }\n\n const parsed = EnvelopeSchema.safeParse(envelopeData)\n if (!parsed.success) {\n return c.json(\n { verified: false, error: 'Invalid envelope format', details: parsed.error.issues },\n 400\n )\n }\n\n const envelope = parsed.data\n const { jws, github: envelopeGithub } = envelope.agentcred\n\n const stored = await c.env.KEYS.get(`key:${envelopeGithub}`)\n if (!stored) {\n return c.json(\n { verified: false, error: `No registered key for user: ${envelopeGithub}` },\n 404\n )\n }\n\n const storedData: StoredKey = JSON.parse(stored)\n\n try {\n const publicKey = await importJWK(\n { ...storedData.public_key, kty: 'OKP', crv: 'Ed25519' },\n 'EdDSA'\n )\n\n const { payload, protectedHeader } = await compactVerify(jws, publicKey)\n\n if (protectedHeader.alg !== 'EdDSA') {\n return c.json({ verified: false, error: 'Invalid algorithm' }, 400)\n }\n\n const payloadStr = new TextDecoder().decode(payload)\n const payloadObj = JSON.parse(payloadStr) as {\n iss: string\n sub: string\n iat: number\n content_hash: string\n content_type?: string\n nonce?: string\n }\n\n const expectedIss = `${envelopeGithub}@agentcred`\n if (payloadObj.iss !== expectedIss) {\n return c.json({ verified: false, error: 'Issuer mismatch' }, 400)\n }\n\n const contentHash = await sha256Hex(envelope.content)\n const expectedHash = `sha256:${contentHash}`\n if (payloadObj.content_hash !== expectedHash) {\n return c.json({ verified: false, error: 'Content hash mismatch - content was tampered' }, 400)\n }\n\n // ±24 hours window per spec section 6.2\n const now = Math.floor(Date.now() / 1000)\n const maxAge = 24 * 60 * 60\n if (Math.abs(now - payloadObj.iat) > maxAge) {\n return c.json({ verified: false, error: 'Signature expired or timestamp out of range' }, 400)\n }\n\n return c.json({\n verified: true,\n github: {\n username: storedData.github,\n id: storedData.github_id,\n },\n agent: envelope.agentcred.agent,\n signed_at: new Date(payloadObj.iat * 1000).toISOString(),\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error'\n return c.json({ verified: false, error: `Verification failed: ${message}` }, 400)\n }\n})\n\napp.get('/v1/health', (c) => {\n return c.json({ status: 'ok', version: '1.0.0' })\n})\n\nexport default app\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@agentcred-ai/api",
3
+ "version": "0.1.0",
4
+ "description": "AgentCred API - Cloudflare Worker for credential verification",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.mjs",
10
+ "require": "./dist/index.cjs"
11
+ }
12
+ },
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "devDependencies": {
20
+ "@cloudflare/vitest-pool-workers": "^0.12.8",
21
+ "@cloudflare/workers-types": "^4.20260131.0",
22
+ "tsup": "^8.0.0",
23
+ "typescript": "^5.8.0",
24
+ "vitest": "^3.0.0",
25
+ "wrangler": "^4.61.1"
26
+ },
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/agentcred-ai/agentcred",
31
+ "directory": "packages/api"
32
+ },
33
+ "homepage": "https://agentcred.dev",
34
+ "bugs": {
35
+ "url": "https://github.com/agentcred-ai/agentcred/issues"
36
+ },
37
+ "keywords": [
38
+ "agentcred",
39
+ "ai-agent",
40
+ "mcp",
41
+ "cryptography",
42
+ "ed25519",
43
+ "identity",
44
+ "signature"
45
+ ],
46
+ "author": "AgentCred Contributors",
47
+ "dependencies": {
48
+ "hono": "^4.11.7",
49
+ "jose": "^6.1.3",
50
+ "zod": "^4.3.6"
51
+ },
52
+ "scripts": {
53
+ "dev": "wrangler dev",
54
+ "build": "tsup",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit"
57
+ }
58
+ }