@orbitmem/relay 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 (86) hide show
  1. package/README.md +108 -0
  2. package/dist/app.d.ts +7 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +27 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/index.d.ts +6 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +24 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/middleware/ed25519-verify.d.ts +14 -0
  11. package/dist/middleware/ed25519-verify.d.ts.map +1 -0
  12. package/dist/middleware/ed25519-verify.js +31 -0
  13. package/dist/middleware/ed25519-verify.js.map +1 -0
  14. package/dist/middleware/erc8128.d.ts +25 -0
  15. package/dist/middleware/erc8128.d.ts.map +1 -0
  16. package/dist/middleware/erc8128.js +196 -0
  17. package/dist/middleware/erc8128.js.map +1 -0
  18. package/dist/middleware/mpp.d.ts +36 -0
  19. package/dist/middleware/mpp.d.ts.map +1 -0
  20. package/dist/middleware/mpp.js +122 -0
  21. package/dist/middleware/mpp.js.map +1 -0
  22. package/dist/middleware/session.d.ts +13 -0
  23. package/dist/middleware/session.d.ts.map +1 -0
  24. package/dist/middleware/session.js +47 -0
  25. package/dist/middleware/session.js.map +1 -0
  26. package/dist/routes/data.d.ts +5 -0
  27. package/dist/routes/data.d.ts.map +1 -0
  28. package/dist/routes/data.js +44 -0
  29. package/dist/routes/data.js.map +1 -0
  30. package/dist/routes/health.d.ts +3 -0
  31. package/dist/routes/health.d.ts.map +1 -0
  32. package/dist/routes/health.js +4 -0
  33. package/dist/routes/health.js.map +1 -0
  34. package/dist/routes/snapshots.d.ts +5 -0
  35. package/dist/routes/snapshots.d.ts.map +1 -0
  36. package/dist/routes/snapshots.js +31 -0
  37. package/dist/routes/snapshots.js.map +1 -0
  38. package/dist/routes/vault.d.ts +6 -0
  39. package/dist/routes/vault.d.ts.map +1 -0
  40. package/dist/routes/vault.js +105 -0
  41. package/dist/routes/vault.js.map +1 -0
  42. package/dist/services/index.d.ts +17 -0
  43. package/dist/services/index.d.ts.map +1 -0
  44. package/dist/services/index.js +60 -0
  45. package/dist/services/index.js.map +1 -0
  46. package/dist/services/live-discovery.d.ts +17 -0
  47. package/dist/services/live-discovery.d.ts.map +1 -0
  48. package/dist/services/live-discovery.js +97 -0
  49. package/dist/services/live-discovery.js.map +1 -0
  50. package/dist/services/live-snapshot.d.ts +18 -0
  51. package/dist/services/live-snapshot.d.ts.map +1 -0
  52. package/dist/services/live-snapshot.js +43 -0
  53. package/dist/services/live-snapshot.js.map +1 -0
  54. package/dist/services/live-vault.d.ts +27 -0
  55. package/dist/services/live-vault.d.ts.map +1 -0
  56. package/dist/services/live-vault.js +73 -0
  57. package/dist/services/live-vault.js.map +1 -0
  58. package/dist/services/mock-discovery.d.ts +14 -0
  59. package/dist/services/mock-discovery.d.ts.map +1 -0
  60. package/dist/services/mock-discovery.js +86 -0
  61. package/dist/services/mock-discovery.js.map +1 -0
  62. package/dist/services/mock-snapshot.d.ts +7 -0
  63. package/dist/services/mock-snapshot.d.ts.map +1 -0
  64. package/dist/services/mock-snapshot.js +30 -0
  65. package/dist/services/mock-snapshot.js.map +1 -0
  66. package/dist/services/mock-vault.d.ts +33 -0
  67. package/dist/services/mock-vault.d.ts.map +1 -0
  68. package/dist/services/mock-vault.js +88 -0
  69. package/dist/services/mock-vault.js.map +1 -0
  70. package/dist/services/orbitdb-peer.d.ts +12 -0
  71. package/dist/services/orbitdb-peer.d.ts.map +1 -0
  72. package/dist/services/orbitdb-peer.js +23 -0
  73. package/dist/services/orbitdb-peer.js.map +1 -0
  74. package/dist/services/plan.d.ts +14 -0
  75. package/dist/services/plan.d.ts.map +1 -0
  76. package/dist/services/plan.js +37 -0
  77. package/dist/services/plan.js.map +1 -0
  78. package/dist/services/test-helpers.d.ts +4 -0
  79. package/dist/services/test-helpers.d.ts.map +1 -0
  80. package/dist/services/test-helpers.js +10 -0
  81. package/dist/services/test-helpers.js.map +1 -0
  82. package/dist/services/types.d.ts +100 -0
  83. package/dist/services/types.d.ts.map +1 -0
  84. package/dist/services/types.js +2 -0
  85. package/dist/services/types.js.map +1 -0
  86. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # @orbitmem/relay
2
+
3
+ HTTP relay server for OrbitMem — authenticated vault access, data discovery, and snapshot archival.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @orbitmem/relay
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Development (hot reload)
15
+ PORT=3000 RELAY_MODE=mock bun run --hot src/index.ts
16
+
17
+ # Production
18
+ bun run dist/index.js
19
+ ```
20
+
21
+ ## Environment Variables
22
+
23
+ | Variable | Default | Description |
24
+ |----------|---------|-------------|
25
+ | `PORT` | `3000` | Server port |
26
+ | `RELAY_MODE` | `mock` | `mock` (in-memory) or `live` (OrbitDB/IPFS) |
27
+
28
+ ## API Endpoints
29
+
30
+ All routes are prefixed with `/v1`.
31
+
32
+ ### Health
33
+
34
+ | Method | Path | Auth | Description |
35
+ |--------|------|------|-------------|
36
+ | GET | `/health` | No | Health check |
37
+
38
+ ### Auth
39
+
40
+ | Method | Path | Auth | Description |
41
+ |--------|------|------|-------------|
42
+ | POST | `/auth/challenge` | No | Generate nonce + message for wallet signing |
43
+ | POST | `/auth/session` | ERC-8128 | Issue session token |
44
+
45
+ ### Vault
46
+
47
+ | Method | Path | Auth | Description |
48
+ |--------|------|------|-------------|
49
+ | GET | `/vault/public/:address/keys` | No | List public keys |
50
+ | GET | `/vault/public/:address/:key` | No | Read public value |
51
+ | POST | `/vault/read` | ERC-8128 | Read encrypted value |
52
+ | POST | `/vault/write` | ERC-8128 | Write vault entry |
53
+ | POST | `/vault/delete` | ERC-8128 | Delete vault entry |
54
+ | POST | `/vault/keys` | ERC-8128 | List vault keys |
55
+ | POST | `/vault/sync` | ERC-8128 | Trigger vault sync |
56
+
57
+ ### Discovery
58
+
59
+ | Method | Path | Auth | Description |
60
+ |--------|------|------|-------------|
61
+ | GET | `/data/stats` | No | Global data statistics |
62
+ | GET | `/data/user/stats` | ERC-8128 | Per-user statistics |
63
+ | GET | `/data/search` | No | Search data registrations |
64
+ | GET | `/data/:dataId/score` | No | Get data quality score |
65
+
66
+ ### Snapshots
67
+
68
+ | Method | Path | Auth | Description |
69
+ |--------|------|------|-------------|
70
+ | GET | `/snapshots` | ERC-8128 | List snapshots |
71
+ | POST | `/snapshots/archive` | ERC-8128 | Archive snapshot |
72
+ | GET | `/snapshots/usage` | ERC-8128 | Storage usage and limit |
73
+
74
+ ## Authentication
75
+
76
+ The relay supports three authentication methods, checked in order:
77
+
78
+ 1. **Bearer Session Token** — Stateless HMAC-SHA256 token (fastest)
79
+ ```
80
+ Authorization: Bearer <token>
81
+ ```
82
+
83
+ 2. **RFC 9421 Signature** — Standard HTTP message signatures with ERC-8128 verification
84
+
85
+ 3. **Legacy X-OrbitMem Headers** — Backward-compatible signed requests
86
+ ```
87
+ X-OrbitMem-Signer: 0x...
88
+ X-OrbitMem-Timestamp: 1234567890
89
+ X-OrbitMem-Nonce: unique-id
90
+ X-OrbitMem-Signature: 0x...
91
+ ```
92
+
93
+ ## Programmatic Usage
94
+
95
+ ```typescript
96
+ import { createApp } from "@orbitmem/relay/app";
97
+ import { createMockServices } from "@orbitmem/relay/services";
98
+
99
+ const services = createMockServices();
100
+ const app = createApp(services);
101
+
102
+ // Use with any Hono-compatible runtime
103
+ export default app;
104
+ ```
105
+
106
+ ## License
107
+
108
+ MIT
package/dist/app.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { Hono } from "hono";
2
+ import { type MPPConfig } from "./middleware/mpp.js";
3
+ import type { RelayServices } from "./services/types.js";
4
+ export declare function buildApp(services: RelayServices, mppConfig?: MPPConfig): Hono;
5
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
6
+ export { app };
7
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAMrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAS7E;AASD,QAAA,MAAM,GAAG,4EAAmD,CAAC;AAE7D,OAAO,EAAE,GAAG,EAAE,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,27 @@
1
+ import { Hono } from "hono";
2
+ import { cors } from "hono/cors";
3
+ import { logger } from "hono/logger";
4
+ import { createDataRoutes } from "./routes/data.js";
5
+ import { healthRoutes } from "./routes/health.js";
6
+ import { createSnapshotRoutes } from "./routes/snapshots.js";
7
+ import { createVaultRoutes } from "./routes/vault.js";
8
+ import { createMockServices } from "./services/index.js";
9
+ export function buildApp(services, mppConfig) {
10
+ const app = new Hono().basePath("/v1");
11
+ app.use(logger());
12
+ app.use(cors());
13
+ app.route("/", healthRoutes);
14
+ app.route("/", createVaultRoutes(services.vault, mppConfig));
15
+ app.route("/", createDataRoutes(services.discovery));
16
+ app.route("/", createSnapshotRoutes(services.snapshot, services.plan));
17
+ return app;
18
+ }
19
+ const defaultMppConfig = process.env.MPP_ACCEPTED_METHODS
20
+ ? {
21
+ acceptedMethods: process.env.MPP_ACCEPTED_METHODS.split(","),
22
+ network: (process.env.MPP_NETWORK ?? "base-sepolia"),
23
+ }
24
+ : undefined;
25
+ const app = buildApp(createMockServices(), defaultMppConfig);
26
+ export { app };
27
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,UAAU,QAAQ,CAAC,QAAuB,EAAE,SAAqB;IACrE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACvC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAClB,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAChB,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC7B,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;IAC7D,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,gBAAgB,GAA0B,OAAO,CAAC,GAAG,CAAC,oBAAoB;IAC9E,CAAC,CAAC;QACE,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAiC;QAC5F,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,cAAc,CAAyB;KAC7E;IACH,CAAC,CAAC,SAAS,CAAC;AAEd,MAAM,GAAG,GAAG,QAAQ,CAAC,kBAAkB,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAE7D,OAAO,EAAE,GAAG,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ port: number;
3
+ fetch: (request: Request, Env?: unknown, executionCtx?: import("hono").ExecutionContext) => Response | Promise<Response>;
4
+ };
5
+ export default _default;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AA4BA,wBAA0C"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ import { buildApp } from "./app.js";
2
+ import { createLiveServices, createMockServices } from "./services/index.js";
3
+ const port = Number(process.env.PORT ?? 3000);
4
+ const mode = process.env.RELAY_MODE ?? "mock";
5
+ console.log(`OrbitMem Relay starting on port ${port} (mode: ${mode})`);
6
+ let app;
7
+ if (mode === "live") {
8
+ const services = await createLiveServices();
9
+ app = buildApp(services);
10
+ }
11
+ else {
12
+ app = buildApp(createMockServices());
13
+ }
14
+ // Graceful shutdown
15
+ process.on("SIGINT", () => {
16
+ console.log("Shutting down...");
17
+ process.exit(0);
18
+ });
19
+ process.on("SIGTERM", () => {
20
+ console.log("Shutting down...");
21
+ process.exit(0);
22
+ });
23
+ export default { port, fetch: app.fetch };
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC;AAE9C,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,WAAW,IAAI,GAAG,CAAC,CAAC;AAEvE,IAAI,GAAgC,CAAC;AAErC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;IACpB,MAAM,QAAQ,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC5C,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;KAAM,CAAC;IACN,GAAG,GAAG,QAAQ,CAAC,kBAAkB,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,oBAAoB;AACpB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;IACxB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACzB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Verify an Ed25519 signature against a payload and public key.
3
+ * Used for Solana wallet authentication in the legacy X-OrbitMem-* header path.
4
+ *
5
+ * The signer address is the hex-encoded Ed25519 public key (not base58).
6
+ * This doubles as both identifier and verification key.
7
+ *
8
+ * @param payload - The signed payload bytes
9
+ * @param signature - The Ed25519 signature (64 bytes)
10
+ * @param publicKeyHex - The signer's public key as hex string (64 hex chars = 32 bytes)
11
+ * @returns true if the signature is valid
12
+ */
13
+ export declare function verifyEd25519(payload: Uint8Array, signature: Uint8Array, publicKeyHex: string): Promise<boolean>;
14
+ //# sourceMappingURL=ed25519-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ed25519-verify.d.ts","sourceRoot":"","sources":["../../src/middleware/ed25519-verify.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAOlB"}
@@ -0,0 +1,31 @@
1
+ import { ed25519 } from "@noble/curves/ed25519.js";
2
+ /**
3
+ * Verify an Ed25519 signature against a payload and public key.
4
+ * Used for Solana wallet authentication in the legacy X-OrbitMem-* header path.
5
+ *
6
+ * The signer address is the hex-encoded Ed25519 public key (not base58).
7
+ * This doubles as both identifier and verification key.
8
+ *
9
+ * @param payload - The signed payload bytes
10
+ * @param signature - The Ed25519 signature (64 bytes)
11
+ * @param publicKeyHex - The signer's public key as hex string (64 hex chars = 32 bytes)
12
+ * @returns true if the signature is valid
13
+ */
14
+ export async function verifyEd25519(payload, signature, publicKeyHex) {
15
+ try {
16
+ const publicKey = hexToBytes(publicKeyHex);
17
+ return ed25519.verify(signature, payload, publicKey);
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ function hexToBytes(hex) {
24
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
25
+ const bytes = new Uint8Array(clean.length / 2);
26
+ for (let i = 0; i < bytes.length; i++) {
27
+ bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
28
+ }
29
+ return bytes;
30
+ }
31
+ //# sourceMappingURL=ed25519-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ed25519-verify.js","sourceRoot":"","sources":["../../src/middleware/ed25519-verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAmB,EACnB,SAAqB,EACrB,YAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ export type ERC8128Env = {
3
+ Variables: {
4
+ signer: string;
5
+ signerFamily: string;
6
+ signerAlgorithm: string;
7
+ };
8
+ };
9
+ /**
10
+ * ERC-8128 signature verification middleware using @slicekit/erc8128.
11
+ *
12
+ * Options:
13
+ * - `verify: 'evm'` — cryptographic verification using viem (secp256k1 recovery)
14
+ * - `verifier: fn` — custom verification callback (legacy, mapped to verifyMessage)
15
+ * - neither — trusts headers without signature check (development/testing)
16
+ * - `required` — whether auth is required (default true)
17
+ * - `replayable` — allow replayable (nonce-less) class-bound signatures (default false)
18
+ */
19
+ export declare function erc8128(opts?: {
20
+ verify?: "evm" | "auto";
21
+ verifier?: (payload: Uint8Array, signature: Uint8Array, algorithm: string) => Promise<boolean>;
22
+ required?: boolean;
23
+ replayable?: boolean;
24
+ }): MiddlewareHandler<ERC8128Env>;
25
+ //# sourceMappingURL=erc8128.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erc8128.d.ts","sourceRoot":"","sources":["../../src/middleware/erc8128.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAM9C,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH,CAAC;AA4BF;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,CAAC,EAAE;IAC7B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAuEhC"}
@@ -0,0 +1,196 @@
1
+ import { createVerifierClient } from "@slicekit/erc8128";
2
+ import { getAddress, verifyMessage } from "viem";
3
+ import { verifyEd25519 } from "./ed25519-verify.js";
4
+ import { verifySessionToken } from "./session.js";
5
+ /**
6
+ * In-memory nonce store for replay protection.
7
+ * Entries auto-expire after their TTL.
8
+ */
9
+ function createMemoryNonceStore() {
10
+ const seen = new Map(); // key -> expiry timestamp (ms)
11
+ function clean() {
12
+ const now = Date.now();
13
+ for (const [key, expiry] of seen) {
14
+ if (now > expiry)
15
+ seen.delete(key);
16
+ }
17
+ }
18
+ return {
19
+ async consume(key, ttlSeconds) {
20
+ clean();
21
+ if (seen.has(key))
22
+ return false; // already consumed
23
+ seen.set(key, Date.now() + ttlSeconds * 1000);
24
+ return true; // newly stored
25
+ },
26
+ };
27
+ }
28
+ const nonceStore = createMemoryNonceStore();
29
+ /**
30
+ * ERC-8128 signature verification middleware using @slicekit/erc8128.
31
+ *
32
+ * Options:
33
+ * - `verify: 'evm'` — cryptographic verification using viem (secp256k1 recovery)
34
+ * - `verifier: fn` — custom verification callback (legacy, mapped to verifyMessage)
35
+ * - neither — trusts headers without signature check (development/testing)
36
+ * - `required` — whether auth is required (default true)
37
+ * - `replayable` — allow replayable (nonce-less) class-bound signatures (default false)
38
+ */
39
+ export function erc8128(opts) {
40
+ const required = opts?.required ?? true;
41
+ const allowReplayable = opts?.replayable ?? true;
42
+ return async (c, next) => {
43
+ // --- Bearer session token path (fastest, no signature check) ---
44
+ const authHeader = c.req.header("Authorization");
45
+ if (authHeader?.startsWith("Bearer ")) {
46
+ const token = authHeader.slice(7);
47
+ const session = await verifySessionToken(token);
48
+ if (session) {
49
+ c.set("signer", session.address);
50
+ c.set("signerFamily", "session");
51
+ c.set("signerAlgorithm", "hmac-sha256");
52
+ await next();
53
+ return;
54
+ }
55
+ // Invalid/expired bearer — check for ERC-8128 fallback headers
56
+ const hasLegacy = !!c.req.header("X-OrbitMem-Signer");
57
+ const hasRfc = !!c.req.header("Signature-Input");
58
+ if (!hasLegacy && !hasRfc) {
59
+ return c.json({ error: "Invalid or expired session token" }, 401);
60
+ }
61
+ // Fall through to ERC-8128 verification below
62
+ }
63
+ // Check if this is a legacy X-OrbitMem-* request or RFC 9421 Signature request
64
+ const hasLegacyHeaders = !!c.req.header("X-OrbitMem-Signer");
65
+ const hasRfc9421Headers = !!c.req.header("Signature-Input");
66
+ if (!hasLegacyHeaders && !hasRfc9421Headers) {
67
+ if (!required) {
68
+ await next();
69
+ return;
70
+ }
71
+ return c.json({ error: "Missing ERC-8128 headers" }, 401);
72
+ }
73
+ // --- Legacy X-OrbitMem-* header path (backward compat) ---
74
+ if (hasLegacyHeaders) {
75
+ return handleLegacy(c, next, opts);
76
+ }
77
+ // --- RFC 9421 / @slicekit/erc8128 path ---
78
+ const shouldVerify = opts?.verify === "evm" || opts?.verifier;
79
+ if (!shouldVerify) {
80
+ // Trust mode: parse keyid from Signature-Input to extract signer address
81
+ const sigInput = c.req.header("Signature-Input") ?? "";
82
+ const keyidMatch = sigInput.match(/keyid="(?:eip155|erc8128):(\d+):(0x[a-fA-F0-9]+)"/);
83
+ if (!keyidMatch) {
84
+ return c.json({ error: "Cannot parse signer from Signature-Input" }, 401);
85
+ }
86
+ c.set("signer", getAddress(keyidMatch[2]));
87
+ c.set("signerFamily", "evm");
88
+ c.set("signerAlgorithm", "ecdsa-secp256k1");
89
+ await next();
90
+ return;
91
+ }
92
+ // Verified mode — use @slicekit/erc8128 verifier
93
+ const result = await verifyWithLibrary(c.req.raw.clone(), allowReplayable);
94
+ if (!result.ok) {
95
+ return c.json({ error: `Invalid signature: ${result.reason}` }, 401);
96
+ }
97
+ c.set("signer", getAddress(result.address));
98
+ c.set("signerFamily", "evm");
99
+ c.set("signerAlgorithm", "ecdsa-secp256k1");
100
+ await next();
101
+ };
102
+ }
103
+ async function verifyWithLibrary(request, allowReplayable) {
104
+ const verifier = createVerifierClient({
105
+ verifyMessage: async ({ address, message, signature }) => {
106
+ return verifyMessage({ address, message, signature });
107
+ },
108
+ nonceStore,
109
+ defaults: {
110
+ replayable: allowReplayable,
111
+ classBoundPolicies: [["@authority"]],
112
+ clockSkewSec: 30,
113
+ maxValiditySec: 3600, // 1 hour max
114
+ },
115
+ });
116
+ return verifier.verifyRequest({ request });
117
+ }
118
+ // --- Legacy handler for backward compatibility with X-OrbitMem-* headers ---
119
+ const legacyNonceCache = new Map();
120
+ const LEGACY_NONCE_TTL = 5 * 60 * 1000;
121
+ const LEGACY_TIMESTAMP_TOLERANCE = 30 * 1000;
122
+ function cleanLegacyNonces() {
123
+ const now = Date.now();
124
+ for (const [nonce, ts] of legacyNonceCache) {
125
+ if (now - ts > LEGACY_NONCE_TTL)
126
+ legacyNonceCache.delete(nonce);
127
+ }
128
+ }
129
+ async function evmVerify(payload, signature, claimedSigner) {
130
+ try {
131
+ const sigHex = `0x${Array.from(signature)
132
+ .map((b) => b.toString(16).padStart(2, "0"))
133
+ .join("")}`;
134
+ return await verifyMessage({
135
+ address: claimedSigner,
136
+ message: { raw: payload },
137
+ signature: sigHex,
138
+ });
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ }
144
+ async function handleLegacy(c, next, opts) {
145
+ const signer = c.req.header("X-OrbitMem-Signer");
146
+ const family = c.req.header("X-OrbitMem-Family");
147
+ const algorithm = c.req.header("X-OrbitMem-Algorithm");
148
+ const timestampStr = c.req.header("X-OrbitMem-Timestamp");
149
+ const nonce = c.req.header("X-OrbitMem-Nonce");
150
+ const signatureHex = c.req.header("X-OrbitMem-Signature");
151
+ if (!signer || !family || !algorithm || !timestampStr || !nonce || !signatureHex) {
152
+ return c.json({ error: "Missing ERC-8128 headers" }, 401);
153
+ }
154
+ const timestamp = Number(timestampStr);
155
+ const now = Date.now();
156
+ if (Math.abs(now - timestamp) > LEGACY_TIMESTAMP_TOLERANCE) {
157
+ return c.json({ error: "Request timestamp out of tolerance" }, 401);
158
+ }
159
+ cleanLegacyNonces();
160
+ if (legacyNonceCache.has(nonce)) {
161
+ return c.json({ error: "Replay detected: nonce already used" }, 401);
162
+ }
163
+ const shouldVerify = opts?.verify === "evm" || opts?.verify === "auto" || opts?.verifier;
164
+ if (shouldVerify) {
165
+ const body = c.req.method !== "GET" ? await c.req.text() : undefined;
166
+ const bodyHash = body
167
+ ? new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(body)))
168
+ : new Uint8Array(0);
169
+ const payload = new TextEncoder().encode(`${c.req.method}\n${c.req.path}\n${timestamp}\n${nonce}\n${Array.from(bodyHash)
170
+ .map((b) => b.toString(16).padStart(2, "0"))
171
+ .join("")}`);
172
+ const signature = new Uint8Array(signatureHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
173
+ let valid;
174
+ if (opts?.verify === "evm" || (opts?.verify === "auto" && family === "evm")) {
175
+ valid = await evmVerify(payload, signature, signer);
176
+ }
177
+ else if (opts?.verify === "auto" && family === "solana" && algorithm === "ed25519") {
178
+ valid = await verifyEd25519(payload, signature, signer);
179
+ }
180
+ else if (opts?.verifier) {
181
+ valid = await opts.verifier(payload, signature, algorithm);
182
+ }
183
+ else {
184
+ valid = false;
185
+ }
186
+ if (!valid) {
187
+ return c.json({ error: "Invalid signature" }, 401);
188
+ }
189
+ }
190
+ legacyNonceCache.set(nonce, Date.now());
191
+ c.set("signer", signer);
192
+ c.set("signerFamily", family);
193
+ c.set("signerAlgorithm", algorithm);
194
+ await next();
195
+ }
196
+ //# sourceMappingURL=erc8128.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erc8128.js","sourceRoot":"","sources":["../../src/middleware/erc8128.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import type { IVaultService } from "../services/types.js";
3
+ export type MPPConfig = {
4
+ acceptedMethods: ("tempo" | "stripe" | "lightning")[];
5
+ network: "base" | "base-sepolia";
6
+ };
7
+ export type MPPEnv = {
8
+ Variables: {
9
+ mppPayment?: {
10
+ producer: string;
11
+ amount: string;
12
+ currency: string;
13
+ method: string;
14
+ };
15
+ };
16
+ };
17
+ /**
18
+ * MPP pricing middleware for vault read routes (GET with :address/:key params).
19
+ */
20
+ export declare function mppPricing(opts: {
21
+ vault: IVaultService;
22
+ config: MPPConfig;
23
+ }): MiddlewareHandler<MPPEnv>;
24
+ /**
25
+ * MPP pricing middleware for POST /vault/read.
26
+ * Extracts vaultAddress from JSON body instead of route params.
27
+ */
28
+ export declare function mppPricingPost(opts: {
29
+ vault: IVaultService;
30
+ config: MPPConfig;
31
+ }): MiddlewareHandler<MPPEnv & {
32
+ Variables: {
33
+ signer: string;
34
+ };
35
+ }>;
36
+ //# sourceMappingURL=mpp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mpp.d.ts","sourceRoot":"","sources":["../../src/middleware/mpp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG;IACtB,eAAe,EAAE,CAAC,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC;IACtD,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,SAAS,EAAE;QACT,UAAU,CAAC,EAAE;YACX,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH,CAAC;AA8BF;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;CACnB,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAwD5B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;CACnB,GAAG,iBAAiB,CAAC,MAAM,GAAG;IAAE,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAsDhE"}
@@ -0,0 +1,122 @@
1
+ /** In-memory LRU pricing cache (address:path -> pricing). TTL: 60s. Only caches positive hits. */
2
+ const pricingCache = new Map();
3
+ const CACHE_TTL = 60_000;
4
+ const MAX_CACHE_SIZE = 1000;
5
+ function getCachedPricing(key) {
6
+ const entry = pricingCache.get(key);
7
+ if (!entry)
8
+ return undefined;
9
+ if (Date.now() > entry.expiry) {
10
+ pricingCache.delete(key);
11
+ return undefined;
12
+ }
13
+ return entry.value;
14
+ }
15
+ function setCachedPricing(key, value) {
16
+ if (pricingCache.size >= MAX_CACHE_SIZE) {
17
+ const keys = Array.from(pricingCache.keys());
18
+ for (let i = 0; i < keys.length / 2; i++) {
19
+ pricingCache.delete(keys[i]);
20
+ }
21
+ }
22
+ pricingCache.set(key, { value, expiry: Date.now() + CACHE_TTL });
23
+ }
24
+ /**
25
+ * MPP pricing middleware for vault read routes (GET with :address/:key params).
26
+ */
27
+ export function mppPricing(opts) {
28
+ const { vault, config } = opts;
29
+ return async (c, next) => {
30
+ const address = c.req.param("address");
31
+ const key = c.req.param("key");
32
+ if (!address || !key) {
33
+ await next();
34
+ return;
35
+ }
36
+ const cacheKey = `${address}:${key}`;
37
+ let pricing = getCachedPricing(cacheKey);
38
+ if (pricing === undefined) {
39
+ const fetched = await vault.getVaultPricing(address, key);
40
+ if (fetched) {
41
+ setCachedPricing(cacheKey, fetched);
42
+ pricing = fetched;
43
+ }
44
+ }
45
+ if (!pricing) {
46
+ await next();
47
+ return;
48
+ }
49
+ const authHeader = c.req.header("Authorization");
50
+ if (!authHeader || !authHeader.startsWith("Payment ")) {
51
+ const challenge = `Payment realm="orbitmem", intent="charge", amount="${pricing.amount}", currency="${pricing.currency}", recipient="${address}", network="${config.network}"`;
52
+ c.header("WWW-Authenticate", challenge);
53
+ return c.json({
54
+ error: "payment_required",
55
+ amount: pricing.amount,
56
+ currency: pricing.currency,
57
+ recipient: address,
58
+ network: config.network,
59
+ methods: config.acceptedMethods,
60
+ }, 402);
61
+ }
62
+ // TODO: Verify payment credential via mppx once SDK API is confirmed.
63
+ // For now, accept any Authorization: Payment header as valid.
64
+ c.set("mppPayment", {
65
+ producer: address,
66
+ amount: pricing.amount,
67
+ currency: pricing.currency,
68
+ method: "unverified",
69
+ });
70
+ await next();
71
+ };
72
+ }
73
+ /**
74
+ * MPP pricing middleware for POST /vault/read.
75
+ * Extracts vaultAddress from JSON body instead of route params.
76
+ */
77
+ export function mppPricingPost(opts) {
78
+ const { vault, config } = opts;
79
+ return async (c, next) => {
80
+ const body = await c.req.json();
81
+ const address = body.vaultAddress ?? c.get("signer");
82
+ const path = body.path;
83
+ if (!address || !path) {
84
+ await next();
85
+ return;
86
+ }
87
+ const cacheKey = `${address}:${path}`;
88
+ let pricing = getCachedPricing(cacheKey);
89
+ if (pricing === undefined) {
90
+ const fetched = await vault.getVaultPricing(address, path);
91
+ if (fetched) {
92
+ setCachedPricing(cacheKey, fetched);
93
+ pricing = fetched;
94
+ }
95
+ }
96
+ if (!pricing) {
97
+ await next();
98
+ return;
99
+ }
100
+ const authHeader = c.req.header("Authorization");
101
+ if (!authHeader || !authHeader.startsWith("Payment ")) {
102
+ const challenge = `Payment realm="orbitmem", intent="charge", amount="${pricing.amount}", currency="${pricing.currency}", recipient="${address}", network="${config.network}"`;
103
+ c.header("WWW-Authenticate", challenge);
104
+ return c.json({
105
+ error: "payment_required",
106
+ amount: pricing.amount,
107
+ currency: pricing.currency,
108
+ recipient: address,
109
+ network: config.network,
110
+ methods: config.acceptedMethods,
111
+ }, 402);
112
+ }
113
+ c.set("mppPayment", {
114
+ producer: address,
115
+ amount: pricing.amount,
116
+ currency: pricing.currency,
117
+ method: "unverified",
118
+ });
119
+ await next();
120
+ };
121
+ }
122
+ //# sourceMappingURL=mpp.js.map