@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 +108 -217
- package/dist/index.cjs +446 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +446 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @aegis-fluxion/core
|
|
2
2
|
|
|
3
|
-
Low-level
|
|
3
|
+
Low-level encrypted WebSocket primitives for the `aegis-fluxion` ecosystem.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
23
|
-
-
|
|
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
|
-
##
|
|
29
|
+
## SecureServer adapter API (horizontal scaling)
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
normalization.
|
|
31
|
+
### Core types
|
|
39
32
|
|
|
40
33
|
```ts
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
74
|
-
context.data = {
|
|
75
|
-
...(context.data as Record<string, unknown>),
|
|
76
|
-
middleware: true
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
});
|
|
51
|
+
### SecureServer hooks
|
|
80
52
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
90
|
-
wsOptions: {
|
|
91
|
-
headers: {
|
|
92
|
-
"x-api-key": "dev-secret"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
58
|
+
### Message normalization helper
|
|
96
59
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.log(response);
|
|
100
|
-
});
|
|
60
|
+
```ts
|
|
61
|
+
import { normalizeSecureServerAdapterMessage } from "@aegis-fluxion/core";
|
|
101
62
|
```
|
|
102
63
|
|
|
103
|
-
|
|
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
|
-
##
|
|
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 {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
96
|
+
async detach(server: SecureServer): Promise<void> {
|
|
97
|
+
if (this.server !== server) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
142
100
|
|
|
143
|
-
|
|
101
|
+
this.server = null;
|
|
102
|
+
InMemoryAdapter.peers.delete(this);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
144
105
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
181
|
-
|
|
122
|
+
server.use(async (context, next) => {
|
|
123
|
+
if (context.phase === "connection") {
|
|
124
|
+
context.metadata.set("auth.role", "operator");
|
|
182
125
|
}
|
|
183
126
|
|
|
184
|
-
|
|
127
|
+
await next();
|
|
185
128
|
});
|
|
186
129
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
##
|
|
148
|
+
## Rate limiting and DDoS shield
|
|
201
149
|
|
|
202
150
|
```ts
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
177
|
+
- `Buffer`
|
|
178
|
+
- `Uint8Array`
|
|
179
|
+
- `Blob`
|
|
285
180
|
|
|
286
|
-
|
|
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
|
|