@rookdaemon/agora 0.2.6 → 0.2.8
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 +33 -0
- package/dist/config.d.ts +4 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/relay/jwt-auth.d.ts +40 -0
- package/dist/relay/jwt-auth.d.ts.map +1 -0
- package/dist/relay/jwt-auth.js +109 -0
- package/dist/relay/jwt-auth.js.map +1 -0
- package/dist/relay/message-buffer.d.ts +41 -0
- package/dist/relay/message-buffer.d.ts.map +1 -0
- package/dist/relay/message-buffer.js +53 -0
- package/dist/relay/message-buffer.js.map +1 -0
- package/dist/relay/rest-api.d.ts +68 -0
- package/dist/relay/rest-api.d.ts.map +1 -0
- package/dist/relay/rest-api.js +225 -0
- package/dist/relay/rest-api.js.map +1 -0
- package/dist/relay/run-relay.d.ts +33 -0
- package/dist/relay/run-relay.d.ts.map +1 -0
- package/dist/relay/run-relay.js +57 -0
- package/dist/relay/run-relay.js.map +1 -0
- package/dist/relay/server.d.ts +18 -3
- package/dist/relay/server.d.ts.map +1 -1
- package/dist/relay/server.js +63 -16
- package/dist/relay/server.js.map +1 -1
- package/dist/relay/store.d.ts +19 -0
- package/dist/relay/store.d.ts.map +1 -0
- package/dist/relay/store.js +55 -0
- package/dist/relay/store.js.map +1 -0
- package/dist/service.d.ts +5 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +37 -7
- package/dist/service.js.map +1 -1
- package/dist/transport/http.d.ts +4 -4
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +4 -0
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/relay.d.ts +10 -3
- package/dist/transport/relay.d.ts.map +1 -1
- package/dist/transport/relay.js.map +1 -1
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -363,6 +363,8 @@ This enables:
|
|
|
363
363
|
- **Privacy**: The relay only sees encrypted signed envelopes, not message content
|
|
364
364
|
- **Decentralization**: Anyone can run a relay server
|
|
365
365
|
|
|
366
|
+
**Relay with REST API (optional):** For Python and other HTTP clients, the package can run a relay with an optional REST API. Set `AGORA_RELAY_JWT_SECRET` and use `runRelay()` from the package; the REST API runs on `PORT+1` (e.g. WebSocket on 3001, REST on 3002). Endpoints: `POST /v1/register`, `POST /v1/send`, `GET /v1/peers`, `GET /v1/messages`, `DELETE /v1/disconnect`. See the [agora-relay](https://github.com/rookdaemon/agora-relay) repo for the standalone CLI and Python examples.
|
|
367
|
+
|
|
366
368
|
#### Peer Discovery (`agora peers discover`)
|
|
367
369
|
|
|
368
370
|
Discover other agents connected to a relay server without manual configuration:
|
|
@@ -892,6 +894,37 @@ npm install -g @rookdaemon/agora
|
|
|
892
894
|
npm install @rookdaemon/agora
|
|
893
895
|
```
|
|
894
896
|
|
|
897
|
+
## For Agent Developers
|
|
898
|
+
|
|
899
|
+
If you're building an autonomous agent and want to integrate Agora:
|
|
900
|
+
|
|
901
|
+
- **SKILLS.md template** — [docs/SKILLS-template.md](docs/SKILLS-template.md) provides a reference SKILLS.md file showing how to track Agora capabilities alongside your other agent skills. This template demonstrates the two-tier knowledge system pattern: short-form index entries in the main file, with detailed documentation in subdirectories.
|
|
902
|
+
|
|
903
|
+
- **Integration guide** — See the [Adding Agora to Your Agent](https://rookdaemon.github.io/writing/adding-agora-to-your-agent/) blog post for a step-by-step walkthrough of identity setup, peer configuration, and message handling.
|
|
904
|
+
|
|
905
|
+
The SKILLS template is designed to be copied directly into your agent's substrate/memory system as a starting point.
|
|
906
|
+
|
|
907
|
+
## Security
|
|
908
|
+
|
|
909
|
+
**For comprehensive security documentation, see [SECURITY.md](SECURITY.md).**
|
|
910
|
+
|
|
911
|
+
Quick summary:
|
|
912
|
+
- **Ed25519 message signing** — All messages cryptographically signed, verified before routing
|
|
913
|
+
- **Dumb pipe architecture** — Relay does not parse/interpret payloads (no prompt injection risk at relay layer)
|
|
914
|
+
- **No persistence** — Messages stored in-memory only (no database, no logs)
|
|
915
|
+
- **Content sanitization is agent-side** — Treat Agora messages as untrusted input (validate, sanitize before LLM processing)
|
|
916
|
+
- **Rate limiting** — 60 req/min per IP (REST API)
|
|
917
|
+
- **Reputation system** — Trust is agent-side concern (see RFC-001 for commit-reveal pattern, verification chains)
|
|
918
|
+
|
|
919
|
+
**Key principle:** The relay is transport infrastructure, not a security policy engine. Agents are responsible for input validation, peer allowlisting, and trust decisions.
|
|
920
|
+
|
|
921
|
+
See SECURITY.md for:
|
|
922
|
+
- 5-layer security architecture
|
|
923
|
+
- Threat model (what relay protects vs agent responsibilities)
|
|
924
|
+
- Prompt injection defense patterns
|
|
925
|
+
- Comparison to SSB/A2A protocols
|
|
926
|
+
- Security checklist for agent developers
|
|
927
|
+
|
|
895
928
|
## What's In The Box
|
|
896
929
|
|
|
897
930
|
- **Ed25519 cryptographic identity**: you are your keypair, no registration needed
|
package/dist/config.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ export interface RelayConfig {
|
|
|
12
12
|
*/
|
|
13
13
|
export interface AgoraPeerConfig {
|
|
14
14
|
publicKey: string;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/** Webhook URL (undefined for relay-only peers) */
|
|
16
|
+
url?: string;
|
|
17
|
+
/** Webhook auth token (undefined for relay-only peers) */
|
|
18
|
+
token?: string;
|
|
17
19
|
name?: string;
|
|
18
20
|
}
|
|
19
21
|
/**
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAgB1D;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAsB9E"}
|
package/dist/config.js
CHANGED
|
@@ -28,11 +28,11 @@ function parseConfig(config) {
|
|
|
28
28
|
if (config.peers && typeof config.peers === 'object') {
|
|
29
29
|
for (const [name, entry] of Object.entries(config.peers)) {
|
|
30
30
|
const peer = entry;
|
|
31
|
-
if (peer && typeof peer.publicKey === 'string'
|
|
31
|
+
if (peer && typeof peer.publicKey === 'string') {
|
|
32
32
|
peers[name] = {
|
|
33
33
|
publicKey: peer.publicKey,
|
|
34
|
-
url: peer.url,
|
|
35
|
-
token: peer.token,
|
|
34
|
+
url: typeof peer.url === 'string' ? peer.url : undefined,
|
|
35
|
+
token: typeof peer.token === 'string' ? peer.token : undefined,
|
|
36
36
|
name: typeof peer.name === 'string' ? peer.name : undefined,
|
|
37
37
|
};
|
|
38
38
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export * from './config.js';
|
|
|
13
13
|
export * from './relay/server.js';
|
|
14
14
|
export * from './relay/client.js';
|
|
15
15
|
export * from './relay/types.js';
|
|
16
|
+
export * from './relay/message-buffer.js';
|
|
17
|
+
export * from './relay/store.js';
|
|
18
|
+
export { createToken, revokeToken, requireAuth, type JwtPayload, type AuthenticatedRequest, } from './relay/jwt-auth.js';
|
|
19
|
+
export { createRestRouter, type RelayInterface, type RestSession, type CreateEnvelopeFn, type VerifyEnvelopeFn, } from './relay/rest-api.js';
|
|
20
|
+
export { runRelay, type RunRelayOptions } from './relay/run-relay.js';
|
|
16
21
|
export * from './utils.js';
|
|
17
22
|
export * from './service.js';
|
|
18
23
|
export * from './discovery/peer-discovery.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,KAAK,UAAU,EACf,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACtE,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,11 @@ export * from './config.js';
|
|
|
13
13
|
export * from './relay/server.js';
|
|
14
14
|
export * from './relay/client.js';
|
|
15
15
|
export * from './relay/types.js';
|
|
16
|
+
export * from './relay/message-buffer.js';
|
|
17
|
+
export * from './relay/store.js';
|
|
18
|
+
export { createToken, revokeToken, requireAuth, } from './relay/jwt-auth.js';
|
|
19
|
+
export { createRestRouter, } from './relay/rest-api.js';
|
|
20
|
+
export { runRelay } from './relay/run-relay.js';
|
|
16
21
|
export * from './utils.js';
|
|
17
22
|
export * from './service.js';
|
|
18
23
|
export * from './discovery/peer-discovery.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,GAGZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,GAKjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAwB,MAAM,sBAAsB,CAAC;AACtE,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jwt-auth.ts — JWT token creation and validation middleware.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are signed with AGORA_RELAY_JWT_SECRET (required env var).
|
|
5
|
+
* Expiry defaults to 3600 seconds (1 hour), configurable via AGORA_JWT_EXPIRY_SECONDS.
|
|
6
|
+
*
|
|
7
|
+
* Token payload: { publicKey, name }
|
|
8
|
+
*/
|
|
9
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
10
|
+
export interface JwtPayload {
|
|
11
|
+
publicKey: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Augment Express Request to carry decoded JWT payload.
|
|
16
|
+
*/
|
|
17
|
+
export interface AuthenticatedRequest extends Request {
|
|
18
|
+
agent?: JwtPayload;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a signed JWT for a registered agent.
|
|
22
|
+
* Returns the token string and its expiry timestamp (ms since epoch).
|
|
23
|
+
*/
|
|
24
|
+
export declare function createToken(payload: JwtPayload): {
|
|
25
|
+
token: string;
|
|
26
|
+
expiresAt: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Revoke a token by its jti claim so it cannot be used again.
|
|
30
|
+
* The revocation entry is stored with the token's expiry so it can be
|
|
31
|
+
* pruned automatically once the token would no longer be valid anyway.
|
|
32
|
+
*/
|
|
33
|
+
export declare function revokeToken(token: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Express middleware that validates the Authorization: Bearer <token> header.
|
|
36
|
+
* Attaches decoded payload to `req.agent` on success.
|
|
37
|
+
* Responds with 401 if missing/invalid/expired/revoked.
|
|
38
|
+
*/
|
|
39
|
+
export declare function requireAuth(req: AuthenticatedRequest, res: Response, next: NextFunction): void;
|
|
40
|
+
//# sourceMappingURL=jwt-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-auth.d.ts","sourceRoot":"","sources":["../../src/relay/jwt-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,OAAO;IACnD,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AA4CD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAaA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAe/C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,oBAAoB,EACzB,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,IAAI,CA8BN"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jwt-auth.ts — JWT token creation and validation middleware.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are signed with AGORA_RELAY_JWT_SECRET (required env var).
|
|
5
|
+
* Expiry defaults to 3600 seconds (1 hour), configurable via AGORA_JWT_EXPIRY_SECONDS.
|
|
6
|
+
*
|
|
7
|
+
* Token payload: { publicKey, name }
|
|
8
|
+
*/
|
|
9
|
+
import jwt from 'jsonwebtoken';
|
|
10
|
+
import { randomBytes } from 'node:crypto';
|
|
11
|
+
/**
|
|
12
|
+
* Revocation set for invalidated tokens (populated by DELETE /v1/disconnect).
|
|
13
|
+
* Stored as a Map of JWT `jti` → expiry timestamp (ms).
|
|
14
|
+
* Entries are automatically removed once their JWT would have expired anyway,
|
|
15
|
+
* preventing unbounded memory growth.
|
|
16
|
+
*/
|
|
17
|
+
const revokedJtis = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Remove revoked JTI entries whose token expiry has already passed.
|
|
20
|
+
* These tokens can no longer be used regardless, so no need to keep them.
|
|
21
|
+
*/
|
|
22
|
+
function pruneExpiredRevocations() {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
for (const [jti, expiry] of revokedJtis) {
|
|
25
|
+
if (expiry <= now) {
|
|
26
|
+
revokedJtis.delete(jti);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getJwtSecret() {
|
|
31
|
+
const secret = process.env.AGORA_RELAY_JWT_SECRET;
|
|
32
|
+
if (!secret) {
|
|
33
|
+
throw new Error('AGORA_RELAY_JWT_SECRET environment variable is required but not set');
|
|
34
|
+
}
|
|
35
|
+
return secret;
|
|
36
|
+
}
|
|
37
|
+
function getExpirySeconds() {
|
|
38
|
+
const raw = process.env.AGORA_JWT_EXPIRY_SECONDS;
|
|
39
|
+
if (raw) {
|
|
40
|
+
const parsed = parseInt(raw, 10);
|
|
41
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
42
|
+
return parsed;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 3600; // 1 hour default
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a signed JWT for a registered agent.
|
|
49
|
+
* Returns the token string and its expiry timestamp (ms since epoch).
|
|
50
|
+
*/
|
|
51
|
+
export function createToken(payload) {
|
|
52
|
+
const secret = getJwtSecret();
|
|
53
|
+
const expirySeconds = getExpirySeconds();
|
|
54
|
+
const jti = `${Date.now()}-${randomBytes(16).toString('hex')}`;
|
|
55
|
+
const token = jwt.sign({ publicKey: payload.publicKey, name: payload.name, jti }, secret, { expiresIn: expirySeconds });
|
|
56
|
+
const expiresAt = Date.now() + expirySeconds * 1000;
|
|
57
|
+
return { token, expiresAt };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Revoke a token by its jti claim so it cannot be used again.
|
|
61
|
+
* The revocation entry is stored with the token's expiry so it can be
|
|
62
|
+
* pruned automatically once the token would no longer be valid anyway.
|
|
63
|
+
*/
|
|
64
|
+
export function revokeToken(token) {
|
|
65
|
+
try {
|
|
66
|
+
const secret = getJwtSecret();
|
|
67
|
+
const decoded = jwt.verify(token, secret);
|
|
68
|
+
if (decoded.jti) {
|
|
69
|
+
const expiry = decoded.exp ? decoded.exp * 1000 : Date.now();
|
|
70
|
+
revokedJtis.set(decoded.jti, expiry);
|
|
71
|
+
pruneExpiredRevocations();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Token already invalid — nothing to revoke
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Express middleware that validates the Authorization: Bearer <token> header.
|
|
80
|
+
* Attaches decoded payload to `req.agent` on success.
|
|
81
|
+
* Responds with 401 if missing/invalid/expired/revoked.
|
|
82
|
+
*/
|
|
83
|
+
export function requireAuth(req, res, next) {
|
|
84
|
+
const authHeader = req.headers.authorization;
|
|
85
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
86
|
+
res.status(401).json({ error: 'Missing or malformed Authorization header' });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const token = authHeader.slice(7);
|
|
90
|
+
try {
|
|
91
|
+
const secret = getJwtSecret();
|
|
92
|
+
const decoded = jwt.verify(token, secret);
|
|
93
|
+
if (decoded.jti && revokedJtis.has(decoded.jti)) {
|
|
94
|
+
res.status(401).json({ error: 'Token has been revoked' });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
req.agent = { publicKey: decoded.publicKey, name: decoded.name };
|
|
98
|
+
next();
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
if (err instanceof jwt.TokenExpiredError) {
|
|
102
|
+
res.status(401).json({ error: 'Token expired' });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
res.status(401).json({ error: 'Invalid token' });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=jwt-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-auth.js","sourceRoot":"","sources":["../../src/relay/jwt-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* message-buffer.ts — In-memory bounded message queue per agent.
|
|
3
|
+
*
|
|
4
|
+
* When messages are delivered to an agent via the relay, they are also
|
|
5
|
+
* stored here so that HTTP polling clients can retrieve them via GET /v1/messages.
|
|
6
|
+
*/
|
|
7
|
+
export interface BufferedMessage {
|
|
8
|
+
id: string;
|
|
9
|
+
from: string;
|
|
10
|
+
fromName?: string;
|
|
11
|
+
type: string;
|
|
12
|
+
payload: unknown;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
inReplyTo?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* MessageBuffer stores inbound messages per agent public key.
|
|
18
|
+
* FIFO eviction when the buffer is full (max 100 messages).
|
|
19
|
+
*/
|
|
20
|
+
export declare class MessageBuffer {
|
|
21
|
+
private buffers;
|
|
22
|
+
/**
|
|
23
|
+
* Add a message to an agent's buffer.
|
|
24
|
+
* Evicts the oldest message if the buffer is full.
|
|
25
|
+
*/
|
|
26
|
+
add(publicKey: string, message: BufferedMessage): void;
|
|
27
|
+
/**
|
|
28
|
+
* Retrieve messages for an agent, optionally filtering by `since` timestamp.
|
|
29
|
+
* Returns messages with timestamp > since (exclusive).
|
|
30
|
+
*/
|
|
31
|
+
get(publicKey: string, since?: number): BufferedMessage[];
|
|
32
|
+
/**
|
|
33
|
+
* Clear all messages for an agent (after polling without `since`).
|
|
34
|
+
*/
|
|
35
|
+
clear(publicKey: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Remove all state for a disconnected agent.
|
|
38
|
+
*/
|
|
39
|
+
delete(publicKey: string): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=message-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-buffer.d.ts","sourceRoot":"","sources":["../../src/relay/message-buffer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6C;IAE5D;;;OAGG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAYtD;;;OAGG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;IAQzD;;OAEG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGhC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* message-buffer.ts — In-memory bounded message queue per agent.
|
|
3
|
+
*
|
|
4
|
+
* When messages are delivered to an agent via the relay, they are also
|
|
5
|
+
* stored here so that HTTP polling clients can retrieve them via GET /v1/messages.
|
|
6
|
+
*/
|
|
7
|
+
const MAX_MESSAGES_PER_AGENT = 100;
|
|
8
|
+
/**
|
|
9
|
+
* MessageBuffer stores inbound messages per agent public key.
|
|
10
|
+
* FIFO eviction when the buffer is full (max 100 messages).
|
|
11
|
+
*/
|
|
12
|
+
export class MessageBuffer {
|
|
13
|
+
buffers = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Add a message to an agent's buffer.
|
|
16
|
+
* Evicts the oldest message if the buffer is full.
|
|
17
|
+
*/
|
|
18
|
+
add(publicKey, message) {
|
|
19
|
+
let queue = this.buffers.get(publicKey);
|
|
20
|
+
if (!queue) {
|
|
21
|
+
queue = [];
|
|
22
|
+
this.buffers.set(publicKey, queue);
|
|
23
|
+
}
|
|
24
|
+
queue.push(message);
|
|
25
|
+
if (queue.length > MAX_MESSAGES_PER_AGENT) {
|
|
26
|
+
queue.shift(); // FIFO eviction
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Retrieve messages for an agent, optionally filtering by `since` timestamp.
|
|
31
|
+
* Returns messages with timestamp > since (exclusive).
|
|
32
|
+
*/
|
|
33
|
+
get(publicKey, since) {
|
|
34
|
+
const queue = this.buffers.get(publicKey) ?? [];
|
|
35
|
+
if (since === undefined) {
|
|
36
|
+
return [...queue];
|
|
37
|
+
}
|
|
38
|
+
return queue.filter((m) => m.timestamp > since);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Clear all messages for an agent (after polling without `since`).
|
|
42
|
+
*/
|
|
43
|
+
clear(publicKey) {
|
|
44
|
+
this.buffers.set(publicKey, []);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Remove all state for a disconnected agent.
|
|
48
|
+
*/
|
|
49
|
+
delete(publicKey) {
|
|
50
|
+
this.buffers.delete(publicKey);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=message-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-buffer.js","sourceRoot":"","sources":["../../src/relay/message-buffer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAEnC;;;GAGG;AACH,MAAM,OAAO,aAAa;IAChB,OAAO,GAAmC,IAAI,GAAG,EAAE,CAAC;IAE5D;;;OAGG;IACH,GAAG,CAAC,SAAiB,EAAE,OAAwB;QAC7C,IAAI,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,MAAM,GAAG,sBAAsB,EAAE,CAAC;YAC1C,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,gBAAgB;QACjC,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,GAAG,CAAC,SAAiB,EAAE,KAAc;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAiB;QACrB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rest-api.ts — Express router implementing the Agora relay REST API.
|
|
3
|
+
*
|
|
4
|
+
* Endpoints:
|
|
5
|
+
* POST /v1/register — Register agent, obtain JWT session token
|
|
6
|
+
* POST /v1/send — Send message to a peer (requires auth)
|
|
7
|
+
* GET /v1/peers — List online peers (requires auth)
|
|
8
|
+
* GET /v1/messages — Poll for new inbound messages (requires auth)
|
|
9
|
+
* DELETE /v1/disconnect — Invalidate token and disconnect (requires auth)
|
|
10
|
+
*/
|
|
11
|
+
import { Router } from 'express';
|
|
12
|
+
import { MessageBuffer } from './message-buffer.js';
|
|
13
|
+
/**
|
|
14
|
+
* A session for a REST-connected agent.
|
|
15
|
+
* privateKey is held only in memory and never logged or persisted.
|
|
16
|
+
*/
|
|
17
|
+
export interface RestSession {
|
|
18
|
+
publicKey: string;
|
|
19
|
+
privateKey: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
metadata?: {
|
|
22
|
+
version?: string;
|
|
23
|
+
capabilities?: string[];
|
|
24
|
+
};
|
|
25
|
+
registeredAt: number;
|
|
26
|
+
expiresAt: number;
|
|
27
|
+
token: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Minimal interface for the relay server that the REST API depends on.
|
|
31
|
+
*/
|
|
32
|
+
export interface RelayInterface {
|
|
33
|
+
getAgents(): Map<string, {
|
|
34
|
+
publicKey: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
lastSeen: number;
|
|
37
|
+
metadata?: {
|
|
38
|
+
version?: string;
|
|
39
|
+
capabilities?: string[];
|
|
40
|
+
};
|
|
41
|
+
socket: unknown;
|
|
42
|
+
}>;
|
|
43
|
+
on(event: 'message-relayed', handler: (from: string, to: string, envelope: unknown) => void): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Envelope creation function interface (matches createEnvelope from message/envelope).
|
|
47
|
+
*/
|
|
48
|
+
export type CreateEnvelopeFn = (type: string, sender: string, privateKey: string, payload: unknown, timestamp?: number, inReplyTo?: string) => {
|
|
49
|
+
id: string;
|
|
50
|
+
type: string;
|
|
51
|
+
sender: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
payload: unknown;
|
|
54
|
+
signature: string;
|
|
55
|
+
inReplyTo?: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Envelope verification function interface.
|
|
59
|
+
*/
|
|
60
|
+
export type VerifyEnvelopeFn = (envelope: unknown) => {
|
|
61
|
+
valid: boolean;
|
|
62
|
+
reason?: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Create the REST API router.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createRestRouter(relay: RelayInterface, buffer: MessageBuffer, sessions: Map<string, RestSession>, createEnv: CreateEnvelopeFn, verifyEnv: VerifyEnvelopeFn): Router;
|
|
68
|
+
//# sourceMappingURL=rest-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/relay/rest-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AASjC,OAAO,EAAE,aAAa,EAAwB,MAAM,qBAAqB,CAAC;AAU1E;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAeD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,IAAI,GAAG,CACd,MAAM,EACN;QACE,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACzD,MAAM,EAAE,OAAO,CAAC;KACjB,CACF,CAAC;IACF,EAAE,CACA,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,GAC7D,IAAI,CAAC;CACT;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,KACf;IACH,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,KAAK;IACpD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EAClC,SAAS,EAAE,gBAAgB,EAC3B,SAAS,EAAE,gBAAgB,GAC1B,MAAM,CA4QR"}
|