@aegis-fluxion/core 0.7.0 → 0.7.2

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 CHANGED
@@ -1,10 +1,8 @@
1
1
  # @aegis-fluxion/core
2
2
 
3
- Low-level E2E-encrypted WebSocket primitives for the `aegis-fluxion` ecosystem.
3
+ Low-level encrypted WebSocket primitives for the `aegis-fluxion` ecosystem.
4
4
 
5
- If you prefer a single user-facing package, use [`aegis-fluxion`](../aegis-fluxion/README.md).
6
-
7
- Version: **0.7.0**
5
+ Version: **0.7.2**
8
6
 
9
7
  ---
10
8
 
@@ -12,15 +10,11 @@ Version: **0.7.0**
12
10
 
13
11
  - Ephemeral ECDH handshake (`prime256v1`)
14
12
  - AES-256-GCM encrypted envelopes
15
- - Encrypted ACK request/response (`Promise` and callback)
16
- - Secure room routing (`join`, `leave`, `leaveAll`, `to(room).emit`)
17
- - Heartbeat and zombie socket cleanup
18
- - Auto-reconnect with fresh re-handshake
19
- - **Binary payload support**: `Buffer`, `Uint8Array`, `Blob`
20
- - **Server middleware pipeline** via `SecureServer.use(...)`
13
+ - ACK request/response (Promise + callback styles)
14
+ - Secure room routing (`join`, `leave`, `leaveAll`, `to(room).emit(...)`)
21
15
  - Middleware phases: `connection`, `incoming`, `outgoing`
22
- - Per-socket middleware metadata available as `SecureServerClient.metadata`
23
- - Optional MCP bridge package: `@aegis-fluxion/mcp-adapter`
16
+ - Rate limiting and DDoS controls per connection and IP
17
+ - **Horizontal scaling hooks** via pluggable `SecureServerAdapter`
24
18
 
25
19
  ---
26
20
 
@@ -32,262 +26,159 @@ npm install @aegis-fluxion/core ws
32
26
 
33
27
  ---
34
28
 
35
- ## Middleware in 0.6.0+
29
+ ## SecureServer adapter API (horizontal scaling)
36
30
 
37
- `SecureServer` now supports phase-based middleware for auth, policy enforcement, and payload
38
- normalization.
31
+ ### Core types
39
32
 
40
33
  ```ts
41
- import { SecureClient, SecureServer } from "@aegis-fluxion/core";
42
-
43
- const server = new SecureServer({ host: "127.0.0.1", port: 8080 });
44
-
45
- server.use(async (context, next) => {
46
- if (context.phase === "connection") {
47
- const rawApiKey = context.request.headers["x-api-key"];
48
- const apiKey = Array.isArray(rawApiKey) ? rawApiKey[0] : rawApiKey;
49
-
50
- if (apiKey !== "dev-secret") {
51
- throw new Error("Unauthorized");
52
- }
53
-
54
- context.metadata.set("role", "editor");
55
- }
56
-
57
- await next();
58
- });
59
-
60
- server.use(async (context, next) => {
61
- if (
62
- context.phase === "incoming" &&
63
- context.event === "post:create" &&
64
- typeof context.data === "object" &&
65
- context.data !== null
66
- ) {
67
- const payload = context.data as { title?: string };
68
- context.data = { title: String(payload.title ?? "").trim() };
69
- }
70
-
71
- await next();
34
+ export interface SecureServerAdapterMessage {
35
+ version: 1;
36
+ originServerId: string;
37
+ scope: "broadcast" | "room";
38
+ event: string;
39
+ data: unknown;
40
+ emittedAt: number;
41
+ room?: string;
42
+ }
43
+
44
+ export interface SecureServerAdapter {
45
+ attach(server: SecureServer): void | Promise<void>;
46
+ publish(message: SecureServerAdapterMessage): void | Promise<void>;
47
+ detach?(server: SecureServer): void | Promise<void>;
48
+ }
49
+ ```
72
50
 
73
- if (context.phase === "outgoing" && context.event === "post:create") {
74
- context.data = {
75
- ...(context.data as Record<string, unknown>),
76
- middleware: true
77
- };
78
- }
79
- });
51
+ ### SecureServer hooks
80
52
 
81
- server.on("post:create", async (payload, client) => {
82
- return {
83
- ok: true,
84
- role: client.metadata.get("role"),
85
- payload
86
- };
87
- });
53
+ - constructor option: `new SecureServer({ ..., adapter })`
54
+ - runtime binding: `await server.setAdapter(adapter)`
55
+ - inbound relay: `await server.handleAdapterMessage(message)`
56
+ - instance identity: `server.serverId`
88
57
 
89
- const client = new SecureClient("ws://127.0.0.1:8080", {
90
- wsOptions: {
91
- headers: {
92
- "x-api-key": "dev-secret"
93
- }
94
- }
95
- });
58
+ ### Message normalization helper
96
59
 
97
- client.on("ready", async () => {
98
- const response = await client.emit("post:create", { title: " Hello " }, { timeoutMs: 1500 });
99
- console.log(response);
100
- });
60
+ ```ts
61
+ import { normalizeSecureServerAdapterMessage } from "@aegis-fluxion/core";
101
62
  ```
102
63
 
103
- Notes:
104
-
105
- - Throwing in `connection` middleware rejects the socket and closes with code `1008`.
106
- - `metadata` is mutable in middleware (`Map`) and exposed read-only on `SecureServerClient`.
64
+ Use it in adapters before delivering inbound Pub/Sub payloads to `SecureServer`.
107
65
 
108
66
  ---
109
67
 
110
- ## MCP Adapter Integration (0.7.0)
111
-
112
- Use `@aegis-fluxion/mcp-adapter` to carry MCP JSON-RPC messages through your encrypted core
113
- transport.
68
+ ## Adapter integration example
114
69
 
115
70
  ```ts
116
- import { SecureClient, SecureServer } from "@aegis-fluxion/core";
117
- import { SecureMCPTransport } from "@aegis-fluxion/mcp-adapter";
118
-
119
- const secureServer = new SecureServer({ host: "127.0.0.1", port: 9091 });
71
+ import {
72
+ SecureServer,
73
+ type SecureServerAdapter,
74
+ type SecureServerAdapterMessage
75
+ } from "@aegis-fluxion/core";
76
+
77
+ class InMemoryAdapter implements SecureServerAdapter {
78
+ private static readonly peers = new Set<InMemoryAdapter>();
79
+ private server: SecureServer | null = null;
80
+
81
+ async attach(server: SecureServer): Promise<void> {
82
+ this.server = server;
83
+ InMemoryAdapter.peers.add(this);
84
+ }
120
85
 
121
- secureServer.on("connection", async (client) => {
122
- const mcpServerTransport = new SecureMCPTransport({
123
- mode: "server",
124
- server: secureServer,
125
- clientId: client.id
126
- });
86
+ async publish(message: SecureServerAdapterMessage): Promise<void> {
87
+ for (const peer of InMemoryAdapter.peers) {
88
+ if (peer === this || !peer.server) {
89
+ continue;
90
+ }
127
91
 
128
- mcpServerTransport.onmessage = async (message) => {
129
- // Forward into your MCP server runtime.
130
- console.log("MCP request on server tunnel", message);
131
- };
132
-
133
- await mcpServerTransport.start();
134
- });
135
-
136
- const secureClient = new SecureClient("ws://127.0.0.1:9091");
92
+ await peer.server.handleAdapterMessage(message);
93
+ }
94
+ }
137
95
 
138
- const mcpClientTransport = new SecureMCPTransport({
139
- mode: "client",
140
- client: secureClient
141
- });
96
+ async detach(server: SecureServer): Promise<void> {
97
+ if (this.server !== server) {
98
+ return;
99
+ }
142
100
 
143
- await mcpClientTransport.start();
101
+ this.server = null;
102
+ InMemoryAdapter.peers.delete(this);
103
+ }
104
+ }
144
105
 
145
- await mcpClientTransport.send({
146
- jsonrpc: "2.0",
147
- id: 100,
148
- method: "tools/list",
149
- params: {}
106
+ const server = new SecureServer({
107
+ host: "127.0.0.1",
108
+ port: 8080,
109
+ adapter: new InMemoryAdapter()
150
110
  });
151
111
  ```
152
112
 
153
113
  ---
154
114
 
155
- ## Binary Data Support
156
-
157
- `@aegis-fluxion/core` supports encrypted binary payload transfer while preserving type fidelity.
158
-
159
- Supported send/receive types:
160
-
161
- - `Buffer`
162
- - `Uint8Array`
163
- - `Blob`
164
-
165
- Binary values can be nested in regular objects and arrays.
166
-
167
- ---
168
-
169
- ## Example: Encrypted Binary Event
115
+ ## Middleware and ACK example
170
116
 
171
117
  ```ts
172
118
  import { SecureClient, SecureServer } from "@aegis-fluxion/core";
173
119
 
174
120
  const server = new SecureServer({ host: "127.0.0.1", port: 8080 });
175
- const client = new SecureClient("ws://127.0.0.1:8080");
176
-
177
- server.on("image:chunk", (data, socket) => {
178
- const chunk = data as Buffer;
179
121
 
180
- if (!Buffer.isBuffer(chunk)) {
181
- throw new Error("Expected Buffer payload.");
122
+ server.use(async (context, next) => {
123
+ if (context.phase === "connection") {
124
+ context.metadata.set("auth.role", "operator");
182
125
  }
183
126
 
184
- socket.emit("image:chunk:ack", chunk);
127
+ await next();
185
128
  });
186
129
 
187
- client.on("ready", () => {
188
- const imageChunk = Buffer.from("89504e470d0a", "hex");
189
- client.emit("image:chunk", imageChunk);
130
+ server.on("jobs:create", async (payload, client) => {
131
+ return {
132
+ ok: true,
133
+ role: client.metadata.get("auth.role"),
134
+ payload
135
+ };
190
136
  });
191
137
 
192
- client.on("image:chunk:ack", (payload) => {
193
- const echoedChunk = payload as Buffer;
194
- console.log("Echoed bytes:", echoedChunk.byteLength);
138
+ const client = new SecureClient("ws://127.0.0.1:8080");
139
+
140
+ client.on("ready", async () => {
141
+ const response = await client.emit("jobs:create", { id: "job-42" }, { timeoutMs: 1200 });
142
+ console.log(response);
195
143
  });
196
144
  ```
197
145
 
198
146
  ---
199
147
 
200
- ## Example: ACK Roundtrip with Mixed Binary Types
148
+ ## Rate limiting and DDoS shield
201
149
 
202
150
  ```ts
203
- server.on("binary:inspect", async (payload) => {
204
- const { file, bytes, blob } = payload as {
205
- file: Buffer;
206
- bytes: Uint8Array;
207
- blob: Blob;
208
- };
209
-
210
- return {
211
- fileBytes: file.byteLength,
212
- bytesBytes: bytes.byteLength,
213
- blobBytes: blob.size
214
- };
215
- });
216
-
217
- client.on("ready", async () => {
218
- const result = await client.emit(
219
- "binary:inspect",
220
- {
221
- file: Buffer.from("file-binary"),
222
- bytes: Uint8Array.from([1, 2, 3, 4]),
223
- blob: new Blob([Buffer.from("blob-binary")], {
224
- type: "application/octet-stream"
225
- })
226
- },
227
- { timeoutMs: 1500 }
228
- );
229
-
230
- console.log(result);
151
+ import { SecureServer } from "@aegis-fluxion/core";
152
+
153
+ const server = new SecureServer({
154
+ host: "127.0.0.1",
155
+ port: 8080,
156
+ rateLimit: {
157
+ enabled: true,
158
+ windowMs: 1_000,
159
+ maxEventsPerConnection: 120,
160
+ maxEventsPerIp: 300,
161
+ action: "throttle", // or "disconnect"
162
+ throttleMs: 150,
163
+ maxThrottleMs: 2_000,
164
+ disconnectAfterViolations: 4,
165
+ disconnectCode: 1013,
166
+ disconnectReason: "Rate limit exceeded. Please retry later."
167
+ }
231
168
  });
232
169
  ```
233
170
 
234
171
  ---
235
172
 
236
- ## API Snapshot
237
-
238
- ### `SecureServer`
239
-
240
- - `on("connection" | "ready" | "disconnect" | "error", handler)`
241
- - `on("custom:event", (data, client) => unknown | Promise<unknown>)`
242
- - `use((context, next) => void | Promise<void>)`
243
- - `emit(event, data): SecureServer`
244
- - `emitTo(clientId, event, data): boolean`
245
- - `emitTo(clientId, event, data, callback): boolean`
246
- - `emitTo(clientId, event, data, options): Promise<unknown>`
247
- - `emitTo(clientId, event, data, options, callback): boolean`
248
- - `to(room).emit(event, data): SecureServer`
249
- - `close(code?, reason?): void`
250
-
251
- ### `SecureServerClient`
252
-
253
- - `id: string`
254
- - `socket: WebSocket`
255
- - `metadata: ReadonlyMap<string, unknown>`
256
- - `emit(event, data, ...ackArgs): boolean | Promise<unknown>`
257
- - `join(room): boolean`
258
- - `leave(room): boolean`
259
- - `leaveAll(): number`
260
-
261
- ### Middleware Types
262
-
263
- - `SecureServerMiddleware`
264
- - `SecureServerMiddlewareContext`
265
- - `SecureServerConnectionMiddlewareContext`
266
- - `SecureServerMessageMiddlewareContext`
267
- - `SecureServerMiddlewareNext`
268
-
269
- ### `SecureClient`
270
-
271
- - `connect(): void`
272
- - `disconnect(code?, reason?): void`
273
- - `isConnected(): boolean`
274
- - `readyState: number | null`
275
- - `emit(event, data): boolean`
276
- - `emit(event, data, callback): boolean`
277
- - `emit(event, data, options): Promise<unknown>`
278
- - `emit(event, data, options, callback): boolean`
279
- - `on("connect" | "ready" | "disconnect" | "error", handler)`
280
- - `on("custom:event", handler)`
173
+ ## Binary payload support
281
174
 
282
- ---
175
+ Supported encrypted payload types:
283
176
 
284
- ## Security Notes
177
+ - `Buffer`
178
+ - `Uint8Array`
179
+ - `Blob`
285
180
 
286
- - All payloads (including binary) are encrypted end-to-end with AES-256-GCM.
287
- - Authentication tags are verified on every packet (tampered packets are dropped).
288
- - Internal transport events are reserved (`__handshake`, `__rpc:req`, `__rpc:res`).
289
- - Pending ACK requests are rejected on timeout/disconnect.
290
- - Middleware-level policy rejection uses WebSocket close code `1008`.
181
+ Binary fields can be nested in standard JSON objects and arrays.
291
182
 
292
183
  ---
293
184