@essentialai/cogent-server 3.4.2 → 3.4.3
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/dist/__tests__/helpers.d.ts +5 -2
- package/dist/__tests__/helpers.d.ts.map +1 -1
- package/dist/__tests__/helpers.js +11 -4
- package/dist/__tests__/helpers.js.map +1 -1
- package/dist/__tests__/services/session-store-contract.d.ts +17 -0
- package/dist/__tests__/services/session-store-contract.d.ts.map +1 -0
- package/dist/__tests__/services/session-store-contract.js +186 -0
- package/dist/__tests__/services/session-store-contract.js.map +1 -0
- package/dist/app.d.ts +9 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +18 -2
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -1
- package/dist/contract/control-plane-contract.d.ts +93 -0
- package/dist/contract/control-plane-contract.d.ts.map +1 -0
- package/dist/contract/control-plane-contract.js +72 -0
- package/dist/contract/control-plane-contract.js.map +1 -0
- package/dist/db/index.d.ts +5 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate-cli.d.ts +2 -0
- package/dist/db/migrate-cli.d.ts.map +1 -0
- package/dist/db/migrate-cli.js +54 -0
- package/dist/db/migrate-cli.js.map +1 -0
- package/dist/db/migrate.d.ts +31 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +98 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrations/0001_init_sessions.down.sql +4 -0
- package/dist/db/migrations/0001_init_sessions.up.sql +46 -0
- package/dist/db/migrations/0002_org_quotas.down.sql +2 -0
- package/dist/db/migrations/0002_org_quotas.up.sql +13 -0
- package/dist/db/pool.d.ts +39 -0
- package/dist/db/pool.d.ts.map +1 -0
- package/dist/db/pool.js +72 -0
- package/dist/db/pool.js.map +1 -0
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +32 -0
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/control-plane-auth.d.ts +17 -0
- package/dist/middleware/control-plane-auth.d.ts.map +1 -0
- package/dist/middleware/control-plane-auth.js +35 -0
- package/dist/middleware/control-plane-auth.js.map +1 -0
- package/dist/routes/control-plane.d.ts +20 -0
- package/dist/routes/control-plane.d.ts.map +1 -0
- package/dist/routes/control-plane.js +122 -0
- package/dist/routes/control-plane.js.map +1 -0
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +18 -0
- package/dist/routes/messages.js.map +1 -1
- package/dist/routes/poll.js +2 -2
- package/dist/routes/poll.js.map +1 -1
- package/dist/routes/sessions.d.ts +22 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +99 -13
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/validation-hook.d.ts +31 -0
- package/dist/routes/validation-hook.d.ts.map +1 -1
- package/dist/routes/validation-hook.js +3 -0
- package/dist/routes/validation-hook.js.map +1 -1
- package/dist/services/auth-service.d.ts +5 -45
- package/dist/services/auth-service.d.ts.map +1 -1
- package/dist/services/auth-service.js +5 -60
- package/dist/services/auth-service.js.map +1 -1
- package/dist/services/connection-manager.d.ts +15 -0
- package/dist/services/connection-manager.d.ts.map +1 -1
- package/dist/services/connection-manager.js +29 -0
- package/dist/services/connection-manager.js.map +1 -1
- package/dist/services/join-rate-limiter.d.ts +50 -0
- package/dist/services/join-rate-limiter.d.ts.map +1 -0
- package/dist/services/join-rate-limiter.js +89 -0
- package/dist/services/join-rate-limiter.js.map +1 -0
- package/dist/services/obs-log.d.ts +51 -0
- package/dist/services/obs-log.d.ts.map +1 -0
- package/dist/services/obs-log.js +93 -0
- package/dist/services/obs-log.js.map +1 -0
- package/dist/services/session-store-memory.d.ts +60 -0
- package/dist/services/session-store-memory.d.ts.map +1 -0
- package/dist/services/session-store-memory.js +189 -0
- package/dist/services/session-store-memory.js.map +1 -0
- package/dist/services/session-store-postgres.d.ts +60 -0
- package/dist/services/session-store-postgres.d.ts.map +1 -0
- package/dist/services/session-store-postgres.js +393 -0
- package/dist/services/session-store-postgres.js.map +1 -0
- package/dist/services/session-store.d.ts +73 -5
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +62 -16
- package/dist/services/session-store.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -6
package/dist/routes/sessions.js
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { zValidator } from "@hono/zod-validator";
|
|
3
3
|
import { BridgeError, ErrorCode, CreateSessionRequestSchema, JoinSessionRequestSchema, generateSessionId, generateSessionLabel, isValidSessionLabel, } from "@essentialai/cogent";
|
|
4
|
-
import {
|
|
4
|
+
import { JoinRateLimiter } from "../services/join-rate-limiter.js";
|
|
5
|
+
import { validationHook, honoSchema } from "./validation-hook.js";
|
|
6
|
+
import { logOrgEvent } from "../services/obs-log.js";
|
|
7
|
+
/**
|
|
8
|
+
* Best-effort client IP for the rate limiter's per-source tier.
|
|
9
|
+
*
|
|
10
|
+
* Prefer `X-Real-IP` (our edge proxy OVERWRITES it with the real peer) and the
|
|
11
|
+
* RIGHTMOST `X-Forwarded-For` entry (the hop our proxy appends) — never the
|
|
12
|
+
* leftmost XFF value, which is attacker-supplied. This is only the UX tier;
|
|
13
|
+
* the spoof-proof guarantee comes from the limiter's channel tier (keyed on the
|
|
14
|
+
* non-spoofable sessionId), so a forged header at worst evades per-IP
|
|
15
|
+
* granularity, not the lockout itself.
|
|
16
|
+
*/
|
|
17
|
+
export function clientIp(c) {
|
|
18
|
+
const realIp = c.req.header("x-real-ip")?.trim();
|
|
19
|
+
if (realIp)
|
|
20
|
+
return realIp;
|
|
21
|
+
const xff = c.req.header("x-forwarded-for");
|
|
22
|
+
if (xff) {
|
|
23
|
+
const parts = xff.split(",").map((s) => s.trim()).filter(Boolean);
|
|
24
|
+
if (parts.length > 0)
|
|
25
|
+
return parts[parts.length - 1];
|
|
26
|
+
}
|
|
27
|
+
return "unknown";
|
|
28
|
+
}
|
|
5
29
|
/**
|
|
6
30
|
* Create session routes sub-router.
|
|
7
31
|
*
|
|
@@ -9,8 +33,13 @@ import { validationHook } from "./validation-hook.js";
|
|
|
9
33
|
* POST /:sessionId/join -> Join an existing session
|
|
10
34
|
*
|
|
11
35
|
* Both endpoints are unauthenticated (they issue tokens, not consume them).
|
|
36
|
+
*
|
|
37
|
+
* @param multiTenant When true, Org_ID (the 3rd join credential) is enforced
|
|
38
|
+
* for channels created with one. When false (free relay),
|
|
39
|
+
* Org_ID is ignored entirely and behavior is unchanged.
|
|
40
|
+
* @param rateLimiter Throttles repeated failed joins (brute-force lockout).
|
|
12
41
|
*/
|
|
13
|
-
export function createSessionRoutes(sessionStore, authService) {
|
|
42
|
+
export function createSessionRoutes(sessionStore, authService, multiTenant, rateLimiter) {
|
|
14
43
|
const sessions = new Hono();
|
|
15
44
|
/**
|
|
16
45
|
* POST / - Create a new session.
|
|
@@ -18,16 +47,26 @@ export function createSessionRoutes(sessionStore, authService) {
|
|
|
18
47
|
* Receives { secret, label? }, bcrypt-hashes the secret, generates a bearer
|
|
19
48
|
* token, creates the session on disk, and returns { sessionId, token, createdAt }.
|
|
20
49
|
*/
|
|
21
|
-
sessions.post("/", zValidator("json", CreateSessionRequestSchema, validationHook), async (c) => {
|
|
22
|
-
const { secret, label } = c.req.valid("json");
|
|
50
|
+
sessions.post("/", zValidator("json", honoSchema(CreateSessionRequestSchema), validationHook), async (c) => {
|
|
51
|
+
const { secret, label, orgId } = c.req.valid("json");
|
|
23
52
|
// Generate unique session ID
|
|
24
53
|
const sessionId = generateSessionId();
|
|
25
|
-
//
|
|
54
|
+
// On a multi-tenant relay, an Org_ID makes this an Org_ID-gated business
|
|
55
|
+
// channel: persist the bcrypt hash (auth) and a stable SHA-256 scope
|
|
56
|
+
// (label namespacing), never the plaintext. The free relay ignores Org_ID
|
|
57
|
+
// entirely (org stays undefined -> labels stay globally scoped, NFR6).
|
|
58
|
+
const org = multiTenant && orgId
|
|
59
|
+
? {
|
|
60
|
+
idHash: await authService.hashSecret(orgId),
|
|
61
|
+
scope: authService.hashToken(orgId),
|
|
62
|
+
}
|
|
63
|
+
: undefined;
|
|
64
|
+
// Auto-generate label if not provided, with collision retry (scoped to the org)
|
|
26
65
|
let sessionLabel = label;
|
|
27
66
|
if (!sessionLabel) {
|
|
28
67
|
for (let i = 0; i < 5; i++) {
|
|
29
68
|
const candidate = generateSessionLabel();
|
|
30
|
-
if (!sessionStore.isLabelTaken(candidate)) {
|
|
69
|
+
if (!sessionStore.isLabelTaken(candidate, org?.scope)) {
|
|
31
70
|
sessionLabel = candidate;
|
|
32
71
|
break;
|
|
33
72
|
}
|
|
@@ -49,7 +88,7 @@ export function createSessionRoutes(sessionStore, authService) {
|
|
|
49
88
|
c.req.header("x-real-ip") ||
|
|
50
89
|
undefined;
|
|
51
90
|
// Persist the session
|
|
52
|
-
const session = await sessionStore.createSession(sessionId, sessionLabel, secretHash, tokenEntry, creatorIp);
|
|
91
|
+
const session = await sessionStore.createSession(sessionId, sessionLabel, secretHash, tokenEntry, creatorIp, org);
|
|
53
92
|
return c.json({
|
|
54
93
|
sessionId,
|
|
55
94
|
token,
|
|
@@ -58,9 +97,13 @@ export function createSessionRoutes(sessionStore, authService) {
|
|
|
58
97
|
}, 201);
|
|
59
98
|
});
|
|
60
99
|
/**
|
|
61
|
-
* GET /resolve/:label - Resolve a session label to its UUID.
|
|
100
|
+
* GET /resolve/:label - Resolve a PUBLIC (free) session label to its UUID.
|
|
62
101
|
*
|
|
63
102
|
* Unauthenticated -- a sessionId alone grants no access (token required).
|
|
103
|
+
* Resolves the public scope ONLY: business (Org_ID-gated) labels are
|
|
104
|
+
* intentionally NOT discoverable here, so a caller cannot enumerate another
|
|
105
|
+
* org's channels by label (per-org isolation, FR6). Business channels are
|
|
106
|
+
* joined by sessionId, provisioned via the control plane / admin (E3).
|
|
64
107
|
* Returns { sessionId, label } or 400/404.
|
|
65
108
|
*/
|
|
66
109
|
sessions.get("/resolve/:label", async (c) => {
|
|
@@ -68,6 +111,7 @@ export function createSessionRoutes(sessionStore, authService) {
|
|
|
68
111
|
if (!isValidSessionLabel(label)) {
|
|
69
112
|
return c.json({ error: "Invalid label format. Must be 3-32 lowercase alphanumeric characters with internal hyphens." }, 400);
|
|
70
113
|
}
|
|
114
|
+
// orgScope omitted -> public scope only; business labels resolve to null.
|
|
71
115
|
const sessionId = sessionStore.getSessionIdByLabel(label);
|
|
72
116
|
if (!sessionId) {
|
|
73
117
|
throw new BridgeError(ErrorCode.SESSION_NOT_FOUND, `No session with label "${label}"`, "Check the label or use the full session ID");
|
|
@@ -80,19 +124,61 @@ export function createSessionRoutes(sessionStore, authService) {
|
|
|
80
124
|
* Receives { secret }, verifies against bcrypt hash, generates a new bearer
|
|
81
125
|
* token, and returns { sessionId, token, peerCount }.
|
|
82
126
|
*/
|
|
83
|
-
sessions.post("/:sessionId/join", zValidator("json", JoinSessionRequestSchema, validationHook), async (c) => {
|
|
127
|
+
sessions.post("/:sessionId/join", zValidator("json", honoSchema(JoinSessionRequestSchema), validationHook), async (c) => {
|
|
84
128
|
const sessionId = c.req.param("sessionId");
|
|
85
|
-
const { secret } = c.req.valid("json");
|
|
129
|
+
const { secret, orgId } = c.req.valid("json");
|
|
130
|
+
// Rate-limit brute-force joins. Tier 1 (source IP + channel) for UX;
|
|
131
|
+
// tier 2 (channel only, non-spoofable) is the security backstop that
|
|
132
|
+
// holds even if X-Forwarded-For / X-Real-IP are forged. The limiter is a
|
|
133
|
+
// Business Edition control: gate it behind multiTenant so the free relay
|
|
134
|
+
// path is unchanged (NFR6 — a free join failure stays 401, never 429).
|
|
135
|
+
const sourceKey = JoinRateLimiter.sourceKey(clientIp(c), sessionId);
|
|
136
|
+
const channelKey = JoinRateLimiter.channelKey(sessionId);
|
|
137
|
+
if (multiTenant)
|
|
138
|
+
rateLimiter.check(sourceKey, channelKey);
|
|
86
139
|
// Look up the session
|
|
87
140
|
const session = await sessionStore.getSession(sessionId);
|
|
88
141
|
if (!session) {
|
|
89
142
|
throw new BridgeError(ErrorCode.SESSION_NOT_FOUND, `Session ${sessionId} not found`, "Check the session ID or create a new session");
|
|
90
143
|
}
|
|
91
|
-
//
|
|
92
|
-
|
|
144
|
+
// A business channel is one that was created WITH an Org_ID (orgIdHash
|
|
145
|
+
// present). Keyed on the stored hash, NOT the live multiTenant flag, so a
|
|
146
|
+
// channel created as business stays Org_ID-gated even if the flag is later
|
|
147
|
+
// toggled off (fail-closed — never silently downgrade to secret-only).
|
|
148
|
+
const isBusiness = session.orgIdHash !== undefined;
|
|
149
|
+
// Enumeration-safe verification: ALWAYS run the same number of bcrypt
|
|
150
|
+
// comparisons regardless of which credential is wrong, so timing leaks
|
|
151
|
+
// nothing. For business channels both the secret AND Org_ID are checked
|
|
152
|
+
// (Org_ID compared even when omitted -> empty string -> false). A wrong
|
|
153
|
+
// Org_ID and a wrong password therefore produce identical work and the
|
|
154
|
+
// identical error below (no oracle).
|
|
155
|
+
const secretOk = await authService.verifySecret(secret, session.secretHash);
|
|
156
|
+
const orgOk = isBusiness
|
|
157
|
+
? await authService.verifySecret(orgId ?? "", session.orgIdHash)
|
|
158
|
+
: true;
|
|
159
|
+
const isValid = secretOk && orgOk;
|
|
93
160
|
if (!isValid) {
|
|
94
|
-
|
|
161
|
+
if (multiTenant)
|
|
162
|
+
rateLimiter.recordFailure(sourceKey, channelKey);
|
|
163
|
+
// Per-org observability (Story 2.7): org-tagged auth failure. NEVER log
|
|
164
|
+
// the secret or Org_ID — only the org_scope tag + that it was a business
|
|
165
|
+
// channel. (Does not break the enumeration-safe single error below.)
|
|
166
|
+
logOrgEvent({
|
|
167
|
+
event: "auth_failure",
|
|
168
|
+
orgScope: session.orgScope ?? null,
|
|
169
|
+
sessionId,
|
|
170
|
+
outcome: "invalid_credentials",
|
|
171
|
+
detail: { phase: "join", isBusiness },
|
|
172
|
+
});
|
|
173
|
+
// Business: ONE generic message for every failure mode (no enumeration).
|
|
174
|
+
// Free: keep the historical message verbatim (NFR6, unchanged behavior).
|
|
175
|
+
throw isBusiness
|
|
176
|
+
? new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid credentials", "Verify the channel secret and Org_ID")
|
|
177
|
+
: new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid session secret", "Verify you are using the correct shared secret for this session");
|
|
95
178
|
}
|
|
179
|
+
// Successful join clears the per-source counter for this (IP, channel).
|
|
180
|
+
if (multiTenant)
|
|
181
|
+
rateLimiter.recordSuccess(sourceKey);
|
|
96
182
|
// Generate a new bearer token for this joining client
|
|
97
183
|
const { token, tokenHash } = authService.generateToken();
|
|
98
184
|
const tokenEntry = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACL,WAAW,EACX,SAAS,EACT,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import type { Context, Env } from "hono";
|
|
2
|
+
import type { zValidator } from "@hono/zod-validator";
|
|
3
|
+
/**
|
|
4
|
+
* Dual-zod-instance type shim for `zValidator`.
|
|
5
|
+
*
|
|
6
|
+
* The server resolves `zod@4.3.6` while `@essentialai/cogent` bundles its OWN
|
|
7
|
+
* nested `zod@4.4.3`. TypeScript compatibility is STRUCTURAL — decided by a
|
|
8
|
+
* type's inferred shape, not its name (vault:
|
|
9
|
+
* typescript-master-vault/concepts/Structural_Type_Compatibility.md). The two
|
|
10
|
+
* copies' `$ZodType`/`$ZodTypeInternals` shapes differ, so a cogent-exported
|
|
11
|
+
* schema is NOT assignable to the `ZodType` shape `@hono/zod-validator` was
|
|
12
|
+
* compiled against — passing it straight in is a `TS2345`/`TS2769` no-overload
|
|
13
|
+
* error, even though both are "zod v4" by name.
|
|
14
|
+
*
|
|
15
|
+
* A type assertion is the sanctioned escape hatch for exactly this case —
|
|
16
|
+
* "integrating with third-party libraries that may not have accurate type
|
|
17
|
+
* definitions" — to be used judiciously, only when necessary (vault:
|
|
18
|
+
* typescript-master-vault/concepts/Type_Assertion.md, Best Practices). So this is
|
|
19
|
+
* ONE narrow, documented boundary cast to the exact type `zValidator` expects
|
|
20
|
+
* (`Parameters<typeof zValidator>[1]`, NOT `any`), not scattered casts. The
|
|
21
|
+
* schemas validate correctly at runtime (covered by the route test suites); only
|
|
22
|
+
* the compile-time type is bridged. Without it `tsc` exits non-zero and
|
|
23
|
+
* `npm run build` (hence the publish gate / image build) fails.
|
|
24
|
+
*
|
|
25
|
+
* Trade-off: the cast collapses the INFERRED output type, so `c.req.valid(...)`
|
|
26
|
+
* at the call sites is `any` rather than the schema's type. Acceptable here — the
|
|
27
|
+
* handlers are small and covered by route tests. Remove once both packages
|
|
28
|
+
* resolve a single zod instance (dedupe the nested copy / align versions).
|
|
29
|
+
*/
|
|
30
|
+
type ZValidatorSchema = Parameters<typeof zValidator>[1];
|
|
31
|
+
export declare function honoSchema(schema: unknown): ZValidatorSchema;
|
|
2
32
|
/**
|
|
3
33
|
* Shared validation hook for @hono/zod-validator.
|
|
4
34
|
*
|
|
@@ -15,4 +45,5 @@ export declare function validationHook(result: {
|
|
|
15
45
|
success: boolean;
|
|
16
46
|
error?: unknown;
|
|
17
47
|
}, c: Context<Env>): Response | void;
|
|
48
|
+
export {};
|
|
18
49
|
//# sourceMappingURL=validation-hook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-hook.d.ts","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"validation-hook.d.ts","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAE5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC7C,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GACd,QAAQ,GAAG,IAAI,CAejB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-hook.js","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"validation-hook.js","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAgC7D,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,OAAO,MAA0B,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA6C,EAC7C,CAAe;IAEf,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;QAC9B,MAAM,OAAO,GACX,QAAQ,YAAY,KAAK;YACvB,CAAC,CAAC,QAAQ,CAAC,OAAO;YAClB,CAAC,CAAC,sBAAsB,CAAC;QAE7B,MAAM,GAAG,GAAG,IAAI,WAAW,CACzB,SAAS,CAAC,aAAa,EACvB,sBAAsB,OAAO,EAAE,EAC/B,gDAAgD,CACjD,CAAC;QACF,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
|
|
@@ -1,48 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Random bearer token generation with SHA-256 hashing for storage
|
|
7
|
-
* - Timing-safe comparison for token verification (every request)
|
|
8
|
-
*
|
|
9
|
-
* bcrypt is used for secrets (slow, salted -- appropriate for passwords).
|
|
10
|
-
* SHA-256 is used for tokens (fast -- tokens are high-entropy random values).
|
|
11
|
-
* Timing-safe comparison prevents timing attacks on token verification.
|
|
2
|
+
* Re-export shim. The hashing/token primitives now live in the shared core
|
|
3
|
+
* (`@essentialai/cogent/auth`) so the relay and the Business Edition control
|
|
4
|
+
* plane hash secrets identically. Behavior is byte-for-byte unchanged; every
|
|
5
|
+
* existing `import { AuthService } from ".../auth-service.js"` keeps working.
|
|
12
6
|
*/
|
|
13
|
-
export
|
|
14
|
-
private readonly bcryptRounds;
|
|
15
|
-
constructor(bcryptRounds: number);
|
|
16
|
-
/**
|
|
17
|
-
* Hash a plaintext secret using bcrypt.
|
|
18
|
-
* Used during session creation and join operations.
|
|
19
|
-
*/
|
|
20
|
-
hashSecret(plainSecret: string): Promise<string>;
|
|
21
|
-
/**
|
|
22
|
-
* Verify a plaintext secret against a bcrypt hash.
|
|
23
|
-
* Used during session join to validate the provided secret.
|
|
24
|
-
*/
|
|
25
|
-
verifySecret(plainSecret: string, hash: string): Promise<boolean>;
|
|
26
|
-
/**
|
|
27
|
-
* Generate a cryptographically random bearer token.
|
|
28
|
-
* Returns both the plaintext token (to send to client) and its
|
|
29
|
-
* SHA-256 hash (to store on disk for lookup).
|
|
30
|
-
*/
|
|
31
|
-
generateToken(): {
|
|
32
|
-
token: string;
|
|
33
|
-
tokenHash: string;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Compute the SHA-256 hash of a token string.
|
|
37
|
-
* Used by auth middleware to look up tokens in the session store index.
|
|
38
|
-
*/
|
|
39
|
-
hashToken(token: string): string;
|
|
40
|
-
/**
|
|
41
|
-
* Timing-safe string comparison.
|
|
42
|
-
* Hashes both strings with SHA-256 to normalize length, then uses
|
|
43
|
-
* crypto.timingSafeEqual on the resulting buffers. This prevents
|
|
44
|
-
* timing attacks regardless of input string lengths.
|
|
45
|
-
*/
|
|
46
|
-
timingSafeCompare(a: string, b: string): boolean;
|
|
47
|
-
}
|
|
7
|
+
export { AuthService } from "@essentialai/cogent/auth";
|
|
48
8
|
//# sourceMappingURL=auth-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -1,63 +1,8 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import bcrypt from "bcryptjs";
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Random bearer token generation with SHA-256 hashing for storage
|
|
9
|
-
* - Timing-safe comparison for token verification (every request)
|
|
10
|
-
*
|
|
11
|
-
* bcrypt is used for secrets (slow, salted -- appropriate for passwords).
|
|
12
|
-
* SHA-256 is used for tokens (fast -- tokens are high-entropy random values).
|
|
13
|
-
* Timing-safe comparison prevents timing attacks on token verification.
|
|
2
|
+
* Re-export shim. The hashing/token primitives now live in the shared core
|
|
3
|
+
* (`@essentialai/cogent/auth`) so the relay and the Business Edition control
|
|
4
|
+
* plane hash secrets identically. Behavior is byte-for-byte unchanged; every
|
|
5
|
+
* existing `import { AuthService } from ".../auth-service.js"` keeps working.
|
|
14
6
|
*/
|
|
15
|
-
export
|
|
16
|
-
bcryptRounds;
|
|
17
|
-
constructor(bcryptRounds) {
|
|
18
|
-
this.bcryptRounds = bcryptRounds;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Hash a plaintext secret using bcrypt.
|
|
22
|
-
* Used during session creation and join operations.
|
|
23
|
-
*/
|
|
24
|
-
async hashSecret(plainSecret) {
|
|
25
|
-
return bcrypt.hash(plainSecret, this.bcryptRounds);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Verify a plaintext secret against a bcrypt hash.
|
|
29
|
-
* Used during session join to validate the provided secret.
|
|
30
|
-
*/
|
|
31
|
-
async verifySecret(plainSecret, hash) {
|
|
32
|
-
return bcrypt.compare(plainSecret, hash);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Generate a cryptographically random bearer token.
|
|
36
|
-
* Returns both the plaintext token (to send to client) and its
|
|
37
|
-
* SHA-256 hash (to store on disk for lookup).
|
|
38
|
-
*/
|
|
39
|
-
generateToken() {
|
|
40
|
-
const token = crypto.randomBytes(32).toString("hex");
|
|
41
|
-
const tokenHash = this.hashToken(token);
|
|
42
|
-
return { token, tokenHash };
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Compute the SHA-256 hash of a token string.
|
|
46
|
-
* Used by auth middleware to look up tokens in the session store index.
|
|
47
|
-
*/
|
|
48
|
-
hashToken(token) {
|
|
49
|
-
return crypto.createHash("sha256").update(token).digest("hex");
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Timing-safe string comparison.
|
|
53
|
-
* Hashes both strings with SHA-256 to normalize length, then uses
|
|
54
|
-
* crypto.timingSafeEqual on the resulting buffers. This prevents
|
|
55
|
-
* timing attacks regardless of input string lengths.
|
|
56
|
-
*/
|
|
57
|
-
timingSafeCompare(a, b) {
|
|
58
|
-
const hashA = crypto.createHash("sha256").update(a).digest();
|
|
59
|
-
const hashB = crypto.createHash("sha256").update(b).digest();
|
|
60
|
-
return crypto.timingSafeEqual(hashA, hashB);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
7
|
+
export { AuthService } from "@essentialai/cogent/auth";
|
|
63
8
|
//# sourceMappingURL=auth-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-service.js","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth-service.js","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -68,6 +68,21 @@ export declare class ConnectionManager {
|
|
|
68
68
|
* Get all connections in a session (flat array across all peers).
|
|
69
69
|
*/
|
|
70
70
|
getAllSessionConnections(sessionId: string): PeerConnection[];
|
|
71
|
+
/**
|
|
72
|
+
* Force-close every WebSocket connection in a session (all peers), returning
|
|
73
|
+
* the number closed. Used when the control plane deletes a channel or rotates
|
|
74
|
+
* an Org_ID: connected agents are dropped so they must rejoin with current
|
|
75
|
+
* credentials. Best-effort — already-closed sockets are ignored. Does not
|
|
76
|
+
* mutate the session store; the caller owns that.
|
|
77
|
+
*
|
|
78
|
+
* Map cleanup is deferred: this does NOT call `removeConnection`. The socket's
|
|
79
|
+
* async `onClose` handler (ws/handler.ts) runs the normal teardown — pruning
|
|
80
|
+
* the connection map, starting the grace timer, and firing `onPeerOffline`.
|
|
81
|
+
* Calling `removeConnection` here as well would double-fire that teardown. So
|
|
82
|
+
* for a brief window after this returns, `getAllSessionConnections` may still
|
|
83
|
+
* list the now-closing sockets (mirrors the existing `closeAll` shutdown path).
|
|
84
|
+
*/
|
|
85
|
+
closeSessionConnections(sessionId: string, code: number, reason: string): number;
|
|
71
86
|
/**
|
|
72
87
|
* Get the list of peer IDs with active connections in a session.
|
|
73
88
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../src/services/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAEhC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,EAAE,EAAE,SAAS,CAAC;IACd,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,iBAAiB,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IAEJ;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IAEJ,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,uEAAuE;IACvE,OAAO,CAAC,aAAa,CAAC,CAA8C;gBAExD,cAAc,GAAE,MAAe;IAI3C;;;OAGG;IACH,gBAAgB,CACd,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GACpD,IAAI;IAIP;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IA4BzC;;;;OAIG;IACH,gBAAgB,CACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,SAAS,GACZ,IAAI;IA4BP;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE;IAMnE;;OAEG;IACH,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAW7D;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAMhD;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAYnE;;;OAGG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI;IAcP;;;;;;;;OAQG;IACH,4BAA4B,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GACpC,IAAI;IAcP;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAOvD;;;OAGG;IACH,sBAAsB,IAAI,MAAM;IAQhC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAwB5C;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAUzB"}
|
|
1
|
+
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../src/services/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAEhC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,EAAE,EAAE,SAAS,CAAC;IACd,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,iBAAiB,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IAEJ;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IAEJ,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,uEAAuE;IACvE,OAAO,CAAC,aAAa,CAAC,CAA8C;gBAExD,cAAc,GAAE,MAAe;IAI3C;;;OAGG;IACH,gBAAgB,CACd,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GACpD,IAAI;IAIP;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IA4BzC;;;;OAIG;IACH,gBAAgB,CACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,SAAS,GACZ,IAAI;IA4BP;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE;IAMnE;;OAEG;IACH,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAW7D;;;;;;;;;;;;;OAaG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAehF;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAMhD;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAYnE;;;OAGG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI;IAcP;;;;;;;;OAQG;IACH,4BAA4B,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GACpC,IAAI;IAcP;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAOvD;;;OAGG;IACH,sBAAsB,IAAI,MAAM;IAQhC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAwB5C;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAUzB"}
|
|
@@ -111,6 +111,35 @@ export class ConnectionManager {
|
|
|
111
111
|
}
|
|
112
112
|
return result;
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Force-close every WebSocket connection in a session (all peers), returning
|
|
116
|
+
* the number closed. Used when the control plane deletes a channel or rotates
|
|
117
|
+
* an Org_ID: connected agents are dropped so they must rejoin with current
|
|
118
|
+
* credentials. Best-effort — already-closed sockets are ignored. Does not
|
|
119
|
+
* mutate the session store; the caller owns that.
|
|
120
|
+
*
|
|
121
|
+
* Map cleanup is deferred: this does NOT call `removeConnection`. The socket's
|
|
122
|
+
* async `onClose` handler (ws/handler.ts) runs the normal teardown — pruning
|
|
123
|
+
* the connection map, starting the grace timer, and firing `onPeerOffline`.
|
|
124
|
+
* Calling `removeConnection` here as well would double-fire that teardown. So
|
|
125
|
+
* for a brief window after this returns, `getAllSessionConnections` may still
|
|
126
|
+
* list the now-closing sockets (mirrors the existing `closeAll` shutdown path).
|
|
127
|
+
*/
|
|
128
|
+
closeSessionConnections(sessionId, code, reason) {
|
|
129
|
+
const conns = this.getAllSessionConnections(sessionId);
|
|
130
|
+
let closed = 0;
|
|
131
|
+
for (const conn of conns) {
|
|
132
|
+
try {
|
|
133
|
+
clearInterval(conn.heartbeatInterval);
|
|
134
|
+
conn.ws.close(code, reason);
|
|
135
|
+
closed++;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Already closed / closing -- nothing to do.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return closed;
|
|
142
|
+
}
|
|
114
143
|
/**
|
|
115
144
|
* Get the list of peer IDs with active connections in a session.
|
|
116
145
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../../src/services/connection-manager.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../../src/services/connection-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory, two-tier rate limiter for session-join attempts.
|
|
3
|
+
*
|
|
4
|
+
* Tier 1 (per source+channel, `${ip}::${sessionId}`): a low threshold for good
|
|
5
|
+
* UX — a single brute-forcing client trips it quickly.
|
|
6
|
+
*
|
|
7
|
+
* Tier 2 (per channel, `channel::${sessionId}`): a higher threshold that is the
|
|
8
|
+
* SECURITY backstop. The channel id comes from the URL path and cannot be
|
|
9
|
+
* spoofed, so it holds even when an attacker rotates `X-Forwarded-For` /
|
|
10
|
+
* `X-Real-IP` to evade tier 1. `check()` blocks if EITHER tier is locked.
|
|
11
|
+
*
|
|
12
|
+
* Single-node MVP: state is process-local. Multi-node (vNext) moves this to a
|
|
13
|
+
* shared store (Postgres / Redis), at which point tier 1 can key on the
|
|
14
|
+
* trusted-proxy-validated real client IP. The clock is injectable for tests.
|
|
15
|
+
*
|
|
16
|
+
* Tradeoff (accepted): tier 2 means an attacker who knows a sessionId can lock
|
|
17
|
+
* NEW joins on that channel for `lockoutMs` with `channelMaxFailures` bad
|
|
18
|
+
* attempts. Already-joined agents keep their bearer tokens (join issues, does
|
|
19
|
+
* not gate, existing sessions). The higher tier-2 threshold keeps this from
|
|
20
|
+
* tripping on the rare legitimate failure; per-org quotas (E5) bound it further.
|
|
21
|
+
*/
|
|
22
|
+
export declare class JoinRateLimiter {
|
|
23
|
+
private readonly perSourceMax;
|
|
24
|
+
private readonly channelMax;
|
|
25
|
+
private readonly windowMs;
|
|
26
|
+
private readonly lockoutMs;
|
|
27
|
+
private readonly now;
|
|
28
|
+
private readonly attempts;
|
|
29
|
+
constructor(perSourceMax: number, channelMax: number, windowMs: number, lockoutMs: number, now?: () => number);
|
|
30
|
+
/** Tier-1 key: source IP + channel (best-effort; IP may be spoofable). */
|
|
31
|
+
static sourceKey(ip: string, sessionId: string): string;
|
|
32
|
+
/** Tier-2 key: channel only (non-spoofable; the security backstop). */
|
|
33
|
+
static channelKey(sessionId: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Throw RATE_LIMITED if EITHER the source tier or the channel tier is locked.
|
|
36
|
+
* Call BEFORE verifying credentials.
|
|
37
|
+
*/
|
|
38
|
+
check(sourceKey: string, channelKey: string): void;
|
|
39
|
+
/** Record a failed attempt against both tiers. */
|
|
40
|
+
recordFailure(sourceKey: string, channelKey: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Clear the SOURCE tier after a successful join. The channel tier is left to
|
|
43
|
+
* decay with its window — a single success must not reset a channel-wide
|
|
44
|
+
* brute-force counter.
|
|
45
|
+
*/
|
|
46
|
+
recordSuccess(sourceKey: string): void;
|
|
47
|
+
/** Increment a key's failure count within its window; lock past `threshold`. */
|
|
48
|
+
private _fail;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=join-rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/services/join-rate-limiter.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;gBAG3D,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAM,MAAyB;IAStC,0EAA0E;IAC1E,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvD,uEAAuE;IACvE,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI5C;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAiBlD,kDAAkD;IAClD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAK1D;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,gFAAgF;IAChF,OAAO,CAAC,KAAK;CAed"}
|