@sunaiva/gate 1.1.0 → 1.1.4
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/BUSINESS_LICENSE.md +70 -70
- package/CHANGELOG.md +254 -148
- package/LICENSE +0 -0
- package/README.DRAFT.md +418 -0
- package/README.md +46 -26
- package/README.md.bak-v1.0.0-stale-MIT +59 -0
- package/SUPPORT.md +75 -0
- package/TIER_DEFINITIONS.md +161 -0
- package/dist/config/defaults.d.ts +30 -10
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +49 -26
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +0 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +1 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/engine/backend-client.d.ts +0 -0
- package/dist/engine/backend-client.d.ts.map +1 -1
- package/dist/engine/backend-client.js +2 -2
- package/dist/engine/backend-client.js.map +1 -1
- package/dist/engine/hmac-verifier.d.ts +19 -0
- package/dist/engine/hmac-verifier.d.ts.map +1 -1
- package/dist/engine/hmac-verifier.js +1 -3
- package/dist/engine/hmac-verifier.js.map +1 -1
- package/dist/engine/immutability.d.ts +0 -0
- package/dist/engine/immutability.d.ts.map +1 -1
- package/dist/engine/immutability.js +0 -0
- package/dist/engine/immutability.js.map +1 -1
- package/dist/engine/pattern-matcher.d.ts +0 -0
- package/dist/engine/pattern-matcher.d.ts.map +1 -1
- package/dist/engine/pattern-matcher.js +0 -0
- package/dist/engine/pattern-matcher.js.map +1 -1
- package/dist/engine/rule-engine.d.ts +8 -1
- package/dist/engine/rule-engine.d.ts.map +1 -1
- package/dist/engine/rule-engine.js +21 -4
- package/dist/engine/rule-engine.js.map +1 -1
- package/dist/engine/session-state.d.ts +0 -0
- package/dist/engine/session-state.d.ts.map +1 -1
- package/dist/engine/session-state.js +0 -0
- package/dist/engine/session-state.js.map +1 -1
- package/dist/engine/ship-confidence-gate.d.ts +48 -0
- package/dist/engine/ship-confidence-gate.d.ts.map +1 -1
- package/dist/engine/ship-confidence-gate.js +2 -2
- package/dist/engine/ship-confidence-gate.js.map +1 -1
- package/dist/identity/first-run.d.ts +24 -0
- package/dist/identity/first-run.d.ts.map +1 -0
- package/dist/identity/first-run.js +88 -0
- package/dist/identity/first-run.js.map +1 -0
- package/dist/identity/nudge.d.ts +29 -0
- package/dist/identity/nudge.d.ts.map +1 -0
- package/dist/identity/nudge.js +74 -0
- package/dist/identity/nudge.js.map +1 -0
- package/dist/identity/premium-unlock.d.ts +30 -0
- package/dist/identity/premium-unlock.d.ts.map +1 -0
- package/dist/identity/premium-unlock.js +65 -0
- package/dist/identity/premium-unlock.js.map +1 -0
- package/dist/identity/register-client.d.ts +25 -0
- package/dist/identity/register-client.d.ts.map +1 -0
- package/dist/identity/register-client.js +48 -0
- package/dist/identity/register-client.js.map +1 -0
- package/dist/identity/telemetry.d.ts +64 -0
- package/dist/identity/telemetry.d.ts.map +1 -0
- package/dist/identity/telemetry.js +173 -0
- package/dist/identity/telemetry.js.map +1 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +101 -23
- package/dist/rules/categories.json +0 -0
- package/dist/rules/presets.json +0 -0
- package/dist/rules/rules.json +257 -178
- package/dist/tools/audit.d.ts +0 -0
- package/dist/tools/audit.d.ts.map +1 -1
- package/dist/tools/audit.js +0 -0
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/bypass.d.ts +0 -0
- package/dist/tools/bypass.d.ts.map +1 -1
- package/dist/tools/bypass.js +1 -1
- package/dist/tools/bypass.js.map +1 -1
- package/dist/tools/export-attestation.d.ts +45 -0
- package/dist/tools/export-attestation.d.ts.map +1 -0
- package/dist/tools/export-attestation.js +152 -0
- package/dist/tools/export-attestation.js.map +1 -0
- package/dist/tools/rules.d.ts +0 -0
- package/dist/tools/rules.d.ts.map +0 -0
- package/dist/tools/rules.js +0 -0
- package/dist/tools/rules.js.map +0 -0
- package/dist/tools/ship-confidence.d.ts +6 -0
- package/dist/tools/ship-confidence.d.ts.map +1 -1
- package/dist/tools/ship-confidence.js +0 -0
- package/dist/tools/ship-confidence.js.map +1 -1
- package/dist/tools/update.d.ts +0 -0
- package/dist/tools/update.d.ts.map +1 -1
- package/dist/tools/update.js +1 -1
- package/dist/tools/update.js.map +1 -1
- package/dist/tools/validate.d.ts +0 -0
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +1 -1
- package/dist/tools/validate.js.map +1 -1
- package/dist/types/backend.d.ts +1 -1
- package/dist/types/backend.d.ts.map +1 -1
- package/dist/types/backend.js +1 -1
- package/dist/types/backend.js.map +1 -1
- package/package.json +84 -73
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Premium rule unlock — fetch premium rules from backend when API key is present.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - Endpoint TBD: stubbed via SUNAIVA_RULES_ENDPOINT env var.
|
|
6
|
+
* - Default stub: https://sunaivacore.io/api/rules/premium (backend TBD).
|
|
7
|
+
* - On success: merges premium rules into the engine's in-memory rule set.
|
|
8
|
+
* - On any failure: falls back to local-only rules + logs to stderr.
|
|
9
|
+
* - 10s timeout — premium rules are fetched once at startup, not per-request.
|
|
10
|
+
* - FAIL-OPEN: a network failure never blocks the user from using the gate.
|
|
11
|
+
* - Only called when config.api_key is present (non-empty string).
|
|
12
|
+
*/
|
|
13
|
+
import type { Rule } from "../engine/rule-engine.js";
|
|
14
|
+
export interface PremiumUnlockResult {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
rules_fetched: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* fetchPremiumRules — GET premium rules from the backend using the API key.
|
|
21
|
+
*
|
|
22
|
+
* On success: returns the rule array for the caller to merge into the engine.
|
|
23
|
+
* On any failure: returns fail-open result (ok:false, rules_fetched:0).
|
|
24
|
+
* NEVER throws.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fetchPremiumRules(apiKey: string): Promise<{
|
|
27
|
+
result: PremiumUnlockResult;
|
|
28
|
+
rules: Rule[];
|
|
29
|
+
}>;
|
|
30
|
+
//# sourceMappingURL=premium-unlock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"premium-unlock.d.ts","sourceRoot":"","sources":["../../src/identity/premium-unlock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAKrD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC,CAoDzD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Premium rule unlock — fetch premium rules from backend when API key is present.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - Endpoint TBD: stubbed via SUNAIVA_RULES_ENDPOINT env var.
|
|
6
|
+
* - Default stub: https://sunaivacore.io/api/rules/premium (backend TBD).
|
|
7
|
+
* - On success: merges premium rules into the engine's in-memory rule set.
|
|
8
|
+
* - On any failure: falls back to local-only rules + logs to stderr.
|
|
9
|
+
* - 10s timeout — premium rules are fetched once at startup, not per-request.
|
|
10
|
+
* - FAIL-OPEN: a network failure never blocks the user from using the gate.
|
|
11
|
+
* - Only called when config.api_key is present (non-empty string).
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_RULES_ENDPOINT = "https://sunaivacore.io/api/rules/premium";
|
|
14
|
+
const FETCH_TIMEOUT_MS = 10_000;
|
|
15
|
+
/**
|
|
16
|
+
* fetchPremiumRules — GET premium rules from the backend using the API key.
|
|
17
|
+
*
|
|
18
|
+
* On success: returns the rule array for the caller to merge into the engine.
|
|
19
|
+
* On any failure: returns fail-open result (ok:false, rules_fetched:0).
|
|
20
|
+
* NEVER throws.
|
|
21
|
+
*/
|
|
22
|
+
export async function fetchPremiumRules(apiKey) {
|
|
23
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
24
|
+
return {
|
|
25
|
+
result: { ok: false, rules_fetched: 0, error: "empty api_key" },
|
|
26
|
+
rules: [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const endpoint = process.env.SUNAIVA_RULES_ENDPOINT ?? DEFAULT_RULES_ENDPOINT;
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(endpoint, {
|
|
34
|
+
method: "GET",
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Bearer ${apiKey}`,
|
|
37
|
+
"Accept": "application/json",
|
|
38
|
+
"X-Gate-Version": "1.1.4",
|
|
39
|
+
},
|
|
40
|
+
signal: controller.signal,
|
|
41
|
+
});
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
const errMsg = `premium-unlock: backend returned ${response.status}`;
|
|
45
|
+
console.error(`[sunaiva-gate] ${errMsg} — falling back to local-only rules`);
|
|
46
|
+
return { result: { ok: false, rules_fetched: 0, error: errMsg }, rules: [] };
|
|
47
|
+
}
|
|
48
|
+
const data = (await response.json());
|
|
49
|
+
const rules = Array.isArray(data.rules) ? data.rules : [];
|
|
50
|
+
return {
|
|
51
|
+
result: { ok: true, rules_fetched: rules.length },
|
|
52
|
+
rules,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
58
|
+
console.error(`[sunaiva-gate] premium-unlock fetch failed (fail-OPEN): ${errMsg} — local-only rules active`);
|
|
59
|
+
return {
|
|
60
|
+
result: { ok: false, rules_fetched: 0, error: errMsg },
|
|
61
|
+
rules: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=premium-unlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"premium-unlock.js","sourceRoot":"","sources":["../../src/identity/premium-unlock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,sBAAsB,GAAG,0CAA0C,CAAC;AAC1E,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAQhC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc;IAEd,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE;YAC/D,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,sBAAsB,CAAC;IAE/D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAErE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,EAAE;gBACjC,QAAQ,EAAE,kBAAkB;gBAC5B,gBAAgB,EAAE,OAAO;aAC1B;YACD,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,oCAAoC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrE,OAAO,CAAC,KAAK,CAAC,kBAAkB,MAAM,qCAAqC,CAAC,CAAC;YAC7E,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC/E,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;QAC3D,MAAM,KAAK,GAAW,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAElE,OAAO;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE;YACjD,KAAK;SACN,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,MAAM,GACV,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACnD,OAAO,CAAC,KAAK,CACX,2DAA2D,MAAM,4BAA4B,CAC9F,CAAC;QACF,OAAO;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE;YACtD,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration client — POST {email} to the Sunaiva registration endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - Endpoint TBD: stubbed via SUNAIVA_REGISTER_ENDPOINT env var.
|
|
6
|
+
* - Default stub: https://sunaivacore.io/api/register (backend TBD).
|
|
7
|
+
* - Returns {api_key, ok} or throws — CALLER must catch and fail-open.
|
|
8
|
+
* - 5s timeout to avoid hanging in tool-use critical paths.
|
|
9
|
+
* - NEVER includes PII beyond the email the user voluntarily supplies.
|
|
10
|
+
* - FAIL-OPEN: caller wraps in try/catch.
|
|
11
|
+
*/
|
|
12
|
+
export interface RegisterResult {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
api_key?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* registerEmail — POST {email} to the registration endpoint.
|
|
19
|
+
*
|
|
20
|
+
* Throws on network error, non-2xx, or timeout. Caller must catch and fail-open.
|
|
21
|
+
*
|
|
22
|
+
* @param email User email (voluntarily supplied at value capture layer).
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerEmail(email: string): Promise<RegisterResult>;
|
|
25
|
+
//# sourceMappingURL=register-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-client.d.ts","sourceRoot":"","sources":["../../src/identity/register-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAoC1E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration client — POST {email} to the Sunaiva registration endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - Endpoint TBD: stubbed via SUNAIVA_REGISTER_ENDPOINT env var.
|
|
6
|
+
* - Default stub: https://sunaivacore.io/api/register (backend TBD).
|
|
7
|
+
* - Returns {api_key, ok} or throws — CALLER must catch and fail-open.
|
|
8
|
+
* - 5s timeout to avoid hanging in tool-use critical paths.
|
|
9
|
+
* - NEVER includes PII beyond the email the user voluntarily supplies.
|
|
10
|
+
* - FAIL-OPEN: caller wraps in try/catch.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_REGISTER_ENDPOINT = "https://sunaivacore.io/api/register";
|
|
13
|
+
const REGISTER_TIMEOUT_MS = 5_000;
|
|
14
|
+
/**
|
|
15
|
+
* registerEmail — POST {email} to the registration endpoint.
|
|
16
|
+
*
|
|
17
|
+
* Throws on network error, non-2xx, or timeout. Caller must catch and fail-open.
|
|
18
|
+
*
|
|
19
|
+
* @param email User email (voluntarily supplied at value capture layer).
|
|
20
|
+
*/
|
|
21
|
+
export async function registerEmail(email) {
|
|
22
|
+
const endpoint = process.env.SUNAIVA_REGISTER_ENDPOINT ?? DEFAULT_REGISTER_ENDPOINT;
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timer = setTimeout(() => controller.abort(), REGISTER_TIMEOUT_MS);
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(endpoint, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({ email, source: "sunaiva-gate", version: "1.1.4" }),
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
});
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Registration endpoint returned ${response.status}`);
|
|
35
|
+
}
|
|
36
|
+
const data = (await response.json());
|
|
37
|
+
return {
|
|
38
|
+
ok: true,
|
|
39
|
+
api_key: data.api_key,
|
|
40
|
+
message: data.message,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
throw err; // re-throw so caller can fail-open
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=register-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-client.js","sourceRoot":"","sources":["../../src/identity/register-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,yBAAyB,GAAG,qCAAqC,CAAC;AACxE,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAQlC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,yBAAyB,CAAC;IAErE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,mBAAmB,CAAC,CAAC;IAExE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YACzE,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIlC,CAAC;QAEF,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,GAAG,CAAC,CAAC,mCAAmC;IAChD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in anonymous telemetry emit + install-tracking layer.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - NO PII, NO file content, NO action content.
|
|
6
|
+
* - Emits: version, gate_version, agent type, os.platform(), os.release(), counts.
|
|
7
|
+
* - Off by default: only fires when user has opted in (SUNAIVA_TELEMETRY_ON=1).
|
|
8
|
+
* - Kill-switch: SUNAIVA_TELEMETRY_OFF=1 overrides any opt-in.
|
|
9
|
+
* - 2s timeout — telemetry must never delay gate decisions.
|
|
10
|
+
* - FAIL-OPEN: any error is swallowed. This function NEVER throws.
|
|
11
|
+
* - Endpoint stubbed via SUNAIVA_TELEMETRY_ENDPOINT env var.
|
|
12
|
+
*
|
|
13
|
+
* Install-tracking layer (v1.1.4 additions):
|
|
14
|
+
* - anonymousFingerprint(): SHA-256 of stable machine identifiers. NO PII, NO IP.
|
|
15
|
+
* - isGenesisInternal(): detects Genesis development environment.
|
|
16
|
+
* - emitFirstRunIfNeeded(): single-shot install event, fire-and-forget. 3s timeout.
|
|
17
|
+
* Opt-out: SUNAIVA_GATE_TELEMETRY=0 disables completely.
|
|
18
|
+
* Marker: ~/.sunaiva-gate/first-run.json prevents duplicate events.
|
|
19
|
+
*/
|
|
20
|
+
export interface TelemetryEvent {
|
|
21
|
+
/** Gate version emitting this event */
|
|
22
|
+
gate_version: string;
|
|
23
|
+
/** Agent type (claude-code, cursor, etc.) or "unknown" */
|
|
24
|
+
agent: string;
|
|
25
|
+
/** os.platform() — linux, win32, darwin */
|
|
26
|
+
os_platform: string;
|
|
27
|
+
/** os.release() — kernel version */
|
|
28
|
+
os_release: string;
|
|
29
|
+
/** Number of violations in the evaluation (no content) */
|
|
30
|
+
violation_count: number;
|
|
31
|
+
/** Number of warnings in the evaluation (no content) */
|
|
32
|
+
warning_count: number;
|
|
33
|
+
/** Number of rules active in this session */
|
|
34
|
+
active_rule_count: number;
|
|
35
|
+
/** ISO timestamp */
|
|
36
|
+
ts: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* emitEvaluation — send an anonymous telemetry event.
|
|
40
|
+
*
|
|
41
|
+
* Only fires when SUNAIVA_TELEMETRY_ON=1 AND SUNAIVA_TELEMETRY_OFF is not set.
|
|
42
|
+
* FAIL-OPEN: all errors are swallowed. Never throws. Never awaited by caller
|
|
43
|
+
* in a blocking way (fire-and-forget via void).
|
|
44
|
+
*/
|
|
45
|
+
export declare function emitEvaluation(eventSummary: {
|
|
46
|
+
agent: string;
|
|
47
|
+
violation_count: number;
|
|
48
|
+
warning_count: number;
|
|
49
|
+
active_rule_count: number;
|
|
50
|
+
}): void;
|
|
51
|
+
/**
|
|
52
|
+
* emitFirstRunIfNeeded — fire a single anonymous "first_run" install event.
|
|
53
|
+
*
|
|
54
|
+
* Privacy posture:
|
|
55
|
+
* - Payload contains: event="first_run", anonymous fingerprint (32-hex),
|
|
56
|
+
* gate_version, node_version, os_platform, os_release,
|
|
57
|
+
* is_genesis_internal, timestamp. NO PII, NO IP, NO email.
|
|
58
|
+
* - Runs at most once per machine (marker file ~/.sunaiva-gate/first-run.json).
|
|
59
|
+
* - Opt-out: SUNAIVA_GATE_TELEMETRY=0 disables entirely.
|
|
60
|
+
* - Fire-and-forget: caller must NOT await. Never throws, never logs to stderr.
|
|
61
|
+
* - 3-second timeout — must not delay gate startup.
|
|
62
|
+
*/
|
|
63
|
+
export declare function emitFirstRunIfNeeded(version: string): Promise<void>;
|
|
64
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/identity/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAkBH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,GAAG,IAAI,CAUP;AAuFD;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CzE"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in anonymous telemetry emit + install-tracking layer.
|
|
3
|
+
*
|
|
4
|
+
* Design constraints (v1.1.4):
|
|
5
|
+
* - NO PII, NO file content, NO action content.
|
|
6
|
+
* - Emits: version, gate_version, agent type, os.platform(), os.release(), counts.
|
|
7
|
+
* - Off by default: only fires when user has opted in (SUNAIVA_TELEMETRY_ON=1).
|
|
8
|
+
* - Kill-switch: SUNAIVA_TELEMETRY_OFF=1 overrides any opt-in.
|
|
9
|
+
* - 2s timeout — telemetry must never delay gate decisions.
|
|
10
|
+
* - FAIL-OPEN: any error is swallowed. This function NEVER throws.
|
|
11
|
+
* - Endpoint stubbed via SUNAIVA_TELEMETRY_ENDPOINT env var.
|
|
12
|
+
*
|
|
13
|
+
* Install-tracking layer (v1.1.4 additions):
|
|
14
|
+
* - anonymousFingerprint(): SHA-256 of stable machine identifiers. NO PII, NO IP.
|
|
15
|
+
* - isGenesisInternal(): detects Genesis development environment.
|
|
16
|
+
* - emitFirstRunIfNeeded(): single-shot install event, fire-and-forget. 3s timeout.
|
|
17
|
+
* Opt-out: SUNAIVA_GATE_TELEMETRY=0 disables completely.
|
|
18
|
+
* Marker: ~/.sunaiva-gate/first-run.json prevents duplicate events.
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { homedir, hostname, platform, release, userInfo, } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
const GATE_VERSION = "1.1.4";
|
|
25
|
+
const DEFAULT_TELEMETRY_ENDPOINT = "https://gate-telemetry.kinan-ae7.workers.dev/v1/events";
|
|
26
|
+
const TELEMETRY_TIMEOUT_MS = 2_000;
|
|
27
|
+
/**
|
|
28
|
+
* emitEvaluation — send an anonymous telemetry event.
|
|
29
|
+
*
|
|
30
|
+
* Only fires when SUNAIVA_TELEMETRY_ON=1 AND SUNAIVA_TELEMETRY_OFF is not set.
|
|
31
|
+
* FAIL-OPEN: all errors are swallowed. Never throws. Never awaited by caller
|
|
32
|
+
* in a blocking way (fire-and-forget via void).
|
|
33
|
+
*/
|
|
34
|
+
export function emitEvaluation(eventSummary) {
|
|
35
|
+
// Kill-switch always wins
|
|
36
|
+
if (process.env.SUNAIVA_TELEMETRY_OFF === "1")
|
|
37
|
+
return;
|
|
38
|
+
// Must be explicitly opted in — off by default (CORE is free, no data harvesting)
|
|
39
|
+
if (process.env.SUNAIVA_TELEMETRY_ON !== "1")
|
|
40
|
+
return;
|
|
41
|
+
// Fire-and-forget — do not await, do not block gate decisions
|
|
42
|
+
void _sendTelemetry(eventSummary).catch(() => {
|
|
43
|
+
// fail-open: swallow all errors
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function _sendTelemetry(eventSummary) {
|
|
47
|
+
const endpoint = process.env.SUNAIVA_TELEMETRY_ENDPOINT ?? DEFAULT_TELEMETRY_ENDPOINT;
|
|
48
|
+
const payload = {
|
|
49
|
+
gate_version: GATE_VERSION,
|
|
50
|
+
agent: eventSummary.agent,
|
|
51
|
+
os_platform: platform(),
|
|
52
|
+
os_release: release(),
|
|
53
|
+
violation_count: eventSummary.violation_count,
|
|
54
|
+
warning_count: eventSummary.warning_count,
|
|
55
|
+
active_rule_count: eventSummary.active_rule_count,
|
|
56
|
+
ts: new Date().toISOString(),
|
|
57
|
+
};
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
const timer = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
60
|
+
try {
|
|
61
|
+
await fetch(endpoint, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify(payload),
|
|
65
|
+
signal: controller.signal,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Install-tracking layer (v1.1.4)
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
const FIRST_RUN_TIMEOUT_MS = 3_000;
|
|
76
|
+
const FIRST_RUN_MARKER_DIR = join(homedir(), ".sunaiva-gate");
|
|
77
|
+
const FIRST_RUN_MARKER_PATH = join(FIRST_RUN_MARKER_DIR, "first-run.json");
|
|
78
|
+
/**
|
|
79
|
+
* anonymousFingerprint — derive a stable, anonymous machine identifier.
|
|
80
|
+
*
|
|
81
|
+
* Privacy posture:
|
|
82
|
+
* - Input: hostname + username + platform + homedir (all local, no network).
|
|
83
|
+
* - Output: first 32 hex chars of SHA-256 — sufficient to de-duplicate
|
|
84
|
+
* installs, NOT sufficient to reverse-identify a user.
|
|
85
|
+
* - NO IP address, NO email, NO file paths, NO system serial numbers.
|
|
86
|
+
* - Value is stable across gate upgrades on the same machine.
|
|
87
|
+
* - On error returns "unknown" (fail-open).
|
|
88
|
+
*/
|
|
89
|
+
function anonymousFingerprint() {
|
|
90
|
+
try {
|
|
91
|
+
const raw = [
|
|
92
|
+
hostname(),
|
|
93
|
+
userInfo().username,
|
|
94
|
+
platform(),
|
|
95
|
+
homedir(),
|
|
96
|
+
].join("|");
|
|
97
|
+
return createHash("sha256").update(raw).digest("hex").slice(0, 32);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return "unknown";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* isGenesisInternal — returns true when running inside a Genesis development
|
|
105
|
+
* environment.
|
|
106
|
+
*
|
|
107
|
+
* Privacy posture:
|
|
108
|
+
* - Reads only environment variables, no network calls.
|
|
109
|
+
* - Allows the telemetry receiver to filter Genesis-internal noise from
|
|
110
|
+
* real-world install events without any PII signal.
|
|
111
|
+
*/
|
|
112
|
+
function isGenesisInternal() {
|
|
113
|
+
if (process.env.SUNAIVA_GATE_INTERNAL)
|
|
114
|
+
return true;
|
|
115
|
+
if (process.env.GENESIS_SESSION_ID)
|
|
116
|
+
return true;
|
|
117
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR ?? "";
|
|
118
|
+
if (projectDir.includes("genesis-system"))
|
|
119
|
+
return true;
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* emitFirstRunIfNeeded — fire a single anonymous "first_run" install event.
|
|
124
|
+
*
|
|
125
|
+
* Privacy posture:
|
|
126
|
+
* - Payload contains: event="first_run", anonymous fingerprint (32-hex),
|
|
127
|
+
* gate_version, node_version, os_platform, os_release,
|
|
128
|
+
* is_genesis_internal, timestamp. NO PII, NO IP, NO email.
|
|
129
|
+
* - Runs at most once per machine (marker file ~/.sunaiva-gate/first-run.json).
|
|
130
|
+
* - Opt-out: SUNAIVA_GATE_TELEMETRY=0 disables entirely.
|
|
131
|
+
* - Fire-and-forget: caller must NOT await. Never throws, never logs to stderr.
|
|
132
|
+
* - 3-second timeout — must not delay gate startup.
|
|
133
|
+
*/
|
|
134
|
+
export async function emitFirstRunIfNeeded(version) {
|
|
135
|
+
try {
|
|
136
|
+
// Respect explicit opt-out
|
|
137
|
+
if (process.env.SUNAIVA_GATE_TELEMETRY === "0")
|
|
138
|
+
return;
|
|
139
|
+
// Single-shot per machine
|
|
140
|
+
if (existsSync(FIRST_RUN_MARKER_PATH))
|
|
141
|
+
return;
|
|
142
|
+
const payload = {
|
|
143
|
+
event: "first_run",
|
|
144
|
+
fingerprint: anonymousFingerprint(),
|
|
145
|
+
gate_version: version,
|
|
146
|
+
node_version: process.version,
|
|
147
|
+
os_platform: platform(),
|
|
148
|
+
os_release: release(),
|
|
149
|
+
is_genesis_internal: isGenesisInternal(),
|
|
150
|
+
timestamp: new Date().toISOString(),
|
|
151
|
+
};
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
const timer = setTimeout(() => controller.abort(), FIRST_RUN_TIMEOUT_MS);
|
|
154
|
+
try {
|
|
155
|
+
await fetch(DEFAULT_TELEMETRY_ENDPOINT, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: { "Content-Type": "application/json" },
|
|
158
|
+
body: JSON.stringify(payload),
|
|
159
|
+
signal: controller.signal,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
// Write marker only after a successful (non-throwing) POST attempt
|
|
166
|
+
mkdirSync(FIRST_RUN_MARKER_DIR, { recursive: true });
|
|
167
|
+
writeFileSync(FIRST_RUN_MARKER_PATH, JSON.stringify({ first_run_at: new Date().toISOString() }));
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// FAIL-OPEN: telemetry must never surface errors to the caller.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/identity/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,YAAY,GAAG,OAAO,CAAC;AAC7B,MAAM,0BAA0B,GAC9B,wDAAwD,CAAC;AAC3D,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAqBnC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,YAK9B;IACC,0BAA0B;IAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG;QAAE,OAAO;IACtD,kFAAkF;IAClF,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG;QAAE,OAAO;IAErD,8DAA8D;IAC9D,KAAK,cAAc,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QAC3C,gCAAgC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,YAK7B;IACC,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,0BAA0B,CAAC;IAEvE,MAAM,OAAO,GAAmB;QAC9B,YAAY,EAAE,YAAY;QAC1B,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,WAAW,EAAE,QAAQ,EAAE;QACvB,UAAU,EAAE,OAAO,EAAE;QACrB,eAAe,EAAE,YAAY,CAAC,eAAe;QAC7C,aAAa,EAAE,YAAY,CAAC,aAAa;QACzC,iBAAiB,EAAE,YAAY,CAAC,iBAAiB;QACjD,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KAC7B,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAEzE,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,QAAQ,EAAE;YACpB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;AAC9D,MAAM,qBAAqB,GAAG,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,SAAS,oBAAoB;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG;YACV,QAAQ,EAAE;YACV,QAAQ,EAAE,CAAC,QAAQ;YACnB,QAAQ,EAAE;YACV,OAAO,EAAE;SACV,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,iBAAiB;IACxB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;IACxD,IAAI,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe;IACxD,IAAI,CAAC;QACH,2BAA2B;QAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,GAAG;YAAE,OAAO;QAEvD,0BAA0B;QAC1B,IAAI,UAAU,CAAC,qBAAqB,CAAC;YAAE,OAAO;QAE9C,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,WAAW;YAClB,WAAW,EAAE,oBAAoB,EAAE;YACnC,YAAY,EAAE,OAAO;YACrB,YAAY,EAAE,OAAO,CAAC,OAAO;YAC7B,WAAW,EAAE,QAAQ,EAAE;YACvB,UAAU,EAAE,OAAO,EAAE;YACrB,mBAAmB,EAAE,iBAAiB,EAAE;YACxC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,oBAAoB,CAAC,CAAC;QAEzE,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,0BAA0B,EAAE;gBACtC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,mEAAmE;QACnE,SAAS,CAAC,oBAAoB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,aAAa,CACX,qBAAqB,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAC3D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -12,10 +12,14 @@ import { ShipConfidenceGate } from "./engine/ship-confidence-gate.js";
|
|
|
12
12
|
import { evaluateAction } from "./engine/rule-engine.js";
|
|
13
13
|
import { getConstitutionalRuleIds } from "./engine/immutability.js";
|
|
14
14
|
import { DEFAULT_CONFIG } from "./config/defaults.js";
|
|
15
|
+
import { runBridge } from "./events/bridge.js";
|
|
16
|
+
import { SUPPORTED_AGENTS } from "./installer/detect.js";
|
|
17
|
+
import { maybeShowNudge } from "./identity/nudge.js";
|
|
18
|
+
import { emitFirstRunIfNeeded } from "./identity/first-run.js";
|
|
15
19
|
import * as fs from "node:fs";
|
|
16
20
|
import * as path from "node:path";
|
|
17
21
|
import { fileURLToPath } from "node:url";
|
|
18
|
-
const PKG_VERSION = "1.1.
|
|
22
|
+
const PKG_VERSION = "1.1.4";
|
|
19
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
24
|
const server = new Server({ name: "sunaiva-gate", version: PKG_VERSION }, { capabilities: { tools: {} } });
|
|
21
25
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -61,6 +65,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
61
65
|
],
|
|
62
66
|
}));
|
|
63
67
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
68
|
+
// Fire-and-forget first-run ping (MCP server path only — never --smoke-test,
|
|
69
|
+
// --version, or --mcp-bridge). Self-throttles via marker file after first call.
|
|
70
|
+
void emitFirstRunIfNeeded(PKG_VERSION);
|
|
64
71
|
const { name, arguments: args } = req.params;
|
|
65
72
|
try {
|
|
66
73
|
switch (name) {
|
|
@@ -96,7 +103,9 @@ function runSmokeTest() {
|
|
|
96
103
|
try {
|
|
97
104
|
const raw = fs.readFileSync(rulesPath, "utf-8");
|
|
98
105
|
const parsed = JSON.parse(raw);
|
|
99
|
-
const flat = Array.isArray(parsed)
|
|
106
|
+
const flat = Array.isArray(parsed)
|
|
107
|
+
? parsed
|
|
108
|
+
: Object.values(parsed).flat();
|
|
100
109
|
allRules = flat;
|
|
101
110
|
totalRules = flat.length;
|
|
102
111
|
constitutionalCount = flat.filter((r) => r && (r.enforcement === "constitutional" || r.constitutional === true)).length;
|
|
@@ -179,7 +188,9 @@ function runSmokeTest() {
|
|
|
179
188
|
}
|
|
180
189
|
else {
|
|
181
190
|
ok = result.allowed && !wasHard;
|
|
182
|
-
detail = ok
|
|
191
|
+
detail = ok
|
|
192
|
+
? "ALLOW"
|
|
193
|
+
: `expected ALLOW; got violations=${result.violations.map((v) => v.id).join(",")}`;
|
|
183
194
|
}
|
|
184
195
|
checks.push({ label: `Live eval (${tc.label})`, ok, detail });
|
|
185
196
|
}
|
|
@@ -214,7 +225,7 @@ function runSmokeTest() {
|
|
|
214
225
|
}
|
|
215
226
|
console.log(`Status: ${allOk ? "HEALTHY" : missingFiles ? "DEGRADED — MISSING FILES" : "DEGRADED"}`);
|
|
216
227
|
console.log(`Version: ${PKG_VERSION}`);
|
|
217
|
-
console.log(`
|
|
228
|
+
console.log(`Support: support@sunaiva.ai`);
|
|
218
229
|
// Exit code alignment with B3 / §6.1 of the brief:
|
|
219
230
|
// 0 = HEALTHY
|
|
220
231
|
// 1 = DEGRADED (legacy — any failing check)
|
|
@@ -232,27 +243,93 @@ async function main() {
|
|
|
232
243
|
process.exit(0);
|
|
233
244
|
}
|
|
234
245
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
235
|
-
console.log(`@sunaiva/gate v${PKG_VERSION} — MCP enforcement layer for AI agent rules
|
|
236
|
-
|
|
237
|
-
Usage:
|
|
238
|
-
npx @sunaiva/gate Run as MCP server (stdio transport)
|
|
239
|
-
npx @sunaiva/gate --smoke-test Run a local health check and exit
|
|
240
|
-
npx @sunaiva/gate --version Print version and exit
|
|
241
|
-
npx @sunaiva/gate --help Show this message
|
|
242
|
-
|
|
243
|
-
Environment:
|
|
244
|
-
DISABLE_SUNAIVA_GATE=1 Kill-switch: server short-circuits to allow-all
|
|
245
|
-
SUNAIVA_GATE_DRY_RUN=1 Dry-run: evaluate but never block (reports would_have_blocked)
|
|
246
|
-
|
|
247
|
-
MCP integration: add the command to your client's mcpServers config.
|
|
248
|
-
Docs: https://
|
|
249
|
-
|
|
250
|
-
Support: support@sunaivadigital.com`);
|
|
246
|
+
console.log(`@sunaiva/gate v${PKG_VERSION} — MCP enforcement layer for AI agent rules
|
|
247
|
+
|
|
248
|
+
Usage:
|
|
249
|
+
npx @sunaiva/gate Run as MCP server (stdio transport)
|
|
250
|
+
npx @sunaiva/gate --smoke-test Run a local health check and exit
|
|
251
|
+
npx @sunaiva/gate --version Print version and exit
|
|
252
|
+
npx @sunaiva/gate --help Show this message
|
|
253
|
+
|
|
254
|
+
Environment:
|
|
255
|
+
DISABLE_SUNAIVA_GATE=1 Kill-switch: server short-circuits to allow-all
|
|
256
|
+
SUNAIVA_GATE_DRY_RUN=1 Dry-run: evaluate but never block (reports would_have_blocked)
|
|
257
|
+
|
|
258
|
+
MCP integration: add the command to your client's mcpServers config.
|
|
259
|
+
Docs: https://sunaivacore.io/products/gate
|
|
260
|
+
Support: support@sunaiva.ai`);
|
|
251
261
|
process.exit(0);
|
|
252
262
|
}
|
|
253
263
|
if (argv.includes("--smoke-test")) {
|
|
254
264
|
process.exit(runSmokeTest());
|
|
255
265
|
}
|
|
266
|
+
// --mcp-bridge <agent> — production hook path.
|
|
267
|
+
// Every installer shim runs `npx @sunaiva/gate --mcp-bridge <agent>`.
|
|
268
|
+
// The host agent pipes a raw JSON event to stdin; we evaluate it through
|
|
269
|
+
// the wired rule engine (via runBridge → handleValidateAction) and write
|
|
270
|
+
// the verdict JSON to stdout. Exit 0 always (fail-OPEN contract: a gate
|
|
271
|
+
// error must never crash the host agent).
|
|
272
|
+
const bridgeIdx = argv.indexOf("--mcp-bridge");
|
|
273
|
+
if (bridgeIdx !== -1) {
|
|
274
|
+
// v1.1.4: show first-run nudge (fail-open, skippable via SUNAIVA_NUDGE_OFF=1)
|
|
275
|
+
maybeShowNudge();
|
|
276
|
+
const agentArg = argv[bridgeIdx + 1];
|
|
277
|
+
if (!agentArg || !SUPPORTED_AGENTS.includes(agentArg)) {
|
|
278
|
+
const supported = SUPPORTED_AGENTS.join(", ");
|
|
279
|
+
console.error(`[sunaiva-gate v${PKG_VERSION}] --mcp-bridge requires a valid agent name (${supported})`);
|
|
280
|
+
// Fail-OPEN: print an allow envelope so the host can continue.
|
|
281
|
+
console.log(JSON.stringify({
|
|
282
|
+
ok: true,
|
|
283
|
+
event: "unknown",
|
|
284
|
+
source_agent: agentArg ?? "unknown",
|
|
285
|
+
tool: undefined,
|
|
286
|
+
action: undefined,
|
|
287
|
+
session_id: undefined,
|
|
288
|
+
decision: "allow",
|
|
289
|
+
reason: "invalid agent argument — fail-OPEN",
|
|
290
|
+
fail_open: true,
|
|
291
|
+
gate_version: PKG_VERSION,
|
|
292
|
+
}));
|
|
293
|
+
process.exit(0);
|
|
294
|
+
}
|
|
295
|
+
// Read the raw event payload from stdin (the host pipes it in one shot).
|
|
296
|
+
let raw = null;
|
|
297
|
+
try {
|
|
298
|
+
const chunks = [];
|
|
299
|
+
for await (const chunk of process.stdin) {
|
|
300
|
+
chunks.push(chunk);
|
|
301
|
+
}
|
|
302
|
+
const text = Buffer.concat(chunks).toString("utf-8").trim();
|
|
303
|
+
if (text.length > 0) {
|
|
304
|
+
raw = JSON.parse(text);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// Malformed or empty stdin: fall through with raw=null; runBridge/
|
|
309
|
+
// normalizeIncoming handles unknown shapes gracefully (fail-OPEN).
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const verdict = await runBridge(raw, agentArg, PKG_VERSION);
|
|
313
|
+
console.log(JSON.stringify(verdict));
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
// runBridge promises never to throw, but be defensive anyway.
|
|
317
|
+
console.error(`[sunaiva-gate v${PKG_VERSION}] bridge unexpected error (fail-OPEN):`, err instanceof Error ? err.message : String(err));
|
|
318
|
+
console.log(JSON.stringify({
|
|
319
|
+
ok: true,
|
|
320
|
+
event: "unknown",
|
|
321
|
+
source_agent: agentArg,
|
|
322
|
+
tool: undefined,
|
|
323
|
+
action: undefined,
|
|
324
|
+
session_id: undefined,
|
|
325
|
+
decision: "allow",
|
|
326
|
+
reason: `bridge unexpected error (fail-OPEN): ${err instanceof Error ? err.message : String(err)}`,
|
|
327
|
+
fail_open: true,
|
|
328
|
+
gate_version: PKG_VERSION,
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
256
333
|
// --ship-confidence <artifact-id> — standalone CLI gate invocation.
|
|
257
334
|
// Exit 0 = allow, 2 = block, 3 = internal error (fail-OPEN logged).
|
|
258
335
|
// Owned by B2; documented by B5.
|
|
@@ -317,8 +394,8 @@ function failClosed(err) {
|
|
|
317
394
|
// Best-effort audit write — never block the exit on I/O.
|
|
318
395
|
try {
|
|
319
396
|
// Lazy import: keep --help / --version paths from pulling in audit code.
|
|
320
|
-
|
|
321
|
-
|
|
397
|
+
import("./tools/audit.js")
|
|
398
|
+
.then((m) => {
|
|
322
399
|
m.appendAudit({
|
|
323
400
|
timestamp: new Date().toISOString(),
|
|
324
401
|
gate_version: PKG_VERSION,
|
|
@@ -328,7 +405,8 @@ function failClosed(err) {
|
|
|
328
405
|
error_message: msg.slice(0, 500),
|
|
329
406
|
audit_status: isInvalidInput ? "INVALID_INPUT" : "INTERNAL_ERROR",
|
|
330
407
|
});
|
|
331
|
-
})
|
|
408
|
+
})
|
|
409
|
+
.catch(() => { });
|
|
332
410
|
}
|
|
333
411
|
catch {
|
|
334
412
|
// Swallow audit errors — never block the exit code.
|
|
File without changes
|
package/dist/rules/presets.json
CHANGED
|
File without changes
|