@bounded-sh/observe 0.1.0
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 +167 -0
- package/dist/context.d.ts +18 -0
- package/dist/context.js +105 -0
- package/dist/counters.d.ts +34 -0
- package/dist/counters.js +141 -0
- package/dist/denylist.d.ts +7 -0
- package/dist/denylist.js +81 -0
- package/dist/envelope.d.ts +71 -0
- package/dist/envelope.js +20 -0
- package/dist/escort.d.ts +92 -0
- package/dist/escort.js +269 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +274 -0
- package/dist/intercept/fetch.d.ts +2 -0
- package/dist/intercept/fetch.js +534 -0
- package/dist/intercept/http.d.ts +2 -0
- package/dist/intercept/http.js +262 -0
- package/dist/llm-prices.d.ts +136 -0
- package/dist/llm-prices.js +240 -0
- package/dist/manifest.d.ts +168 -0
- package/dist/manifest.js +941 -0
- package/dist/pipeline.d.ts +18 -0
- package/dist/pipeline.js +212 -0
- package/dist/recognize.d.ts +38 -0
- package/dist/recognize.js +185 -0
- package/dist/register.d.ts +9 -0
- package/dist/register.js +138 -0
- package/dist/reporter.d.ts +27 -0
- package/dist/reporter.js +210 -0
- package/dist/shapes.d.ts +15 -0
- package/dist/shapes.js +56 -0
- package/dist/state.d.ts +32 -0
- package/dist/state.js +42 -0
- package/dist/template.d.ts +7 -0
- package/dist/template.js +63 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.js +10 -0
- package/package.json +43 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escort (enforce) config for a route (SPEC §4.2 Mode A, T9). Present only on
|
|
3
|
+
* routes a promoted policy has escorted. When set, the interceptor makes ONE
|
|
4
|
+
* verdict round-trip BEFORE the call fires; the call still fires from the
|
|
5
|
+
* customer's own process (payload never transits Bounded).
|
|
6
|
+
*/
|
|
7
|
+
export interface EscortConfig {
|
|
8
|
+
/** Full URL the interceptor POSTs the candidate intent to for a verdict,
|
|
9
|
+
* e.g. "https://acme-dev.bounded.sh/api/verdict". Sensor-key authed. */
|
|
10
|
+
verdictUrl: string;
|
|
11
|
+
/** Full URL for the fire-and-forget settle report. Defaults to verdictUrl's
|
|
12
|
+
* sibling "/settle". */
|
|
13
|
+
settleUrl?: string;
|
|
14
|
+
/** Enforcement collection (execcfg key), e.g. "stripe_refunds". */
|
|
15
|
+
collection: string;
|
|
16
|
+
/** Rail request-body field -> intent field, e.g. {charge:"chargeId",
|
|
17
|
+
* amount:"amountCents"}. Extracted from the request the app ALREADY built. */
|
|
18
|
+
fieldMap: Record<string, string>;
|
|
19
|
+
/** Local behavior when the verdict endpoint is unreachable (U18). Default
|
|
20
|
+
* "closed" (immediate clean local error); "open" = proceed + flag. */
|
|
21
|
+
failMode: "closed" | "open";
|
|
22
|
+
/** Informational only — the server owns the real approval routing decision. */
|
|
23
|
+
approvalAbove?: {
|
|
24
|
+
field: string;
|
|
25
|
+
limit: number;
|
|
26
|
+
};
|
|
27
|
+
/** Verdict round-trip timeout, ms (default 3000). */
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wanted-identity attribution for a route: an org may WANT a field like
|
|
32
|
+
* customer_email for per-user limits (per-actor enforcement + rollups). The
|
|
33
|
+
* value flows through the ATTRIBUTION channel (event.onBehalfOf) — NEVER the
|
|
34
|
+
* telemetry capture lists. Hashed by default IN-PROCESS (the raw value never
|
|
35
|
+
* leaves); raw values ride only on an explicit owner opt-in (hash:false).
|
|
36
|
+
*/
|
|
37
|
+
export interface IdentityFieldConfig {
|
|
38
|
+
/** Request-body field (dot path) holding the per-user principal, e.g. "customer_email". */
|
|
39
|
+
source: string;
|
|
40
|
+
/** The only supported target: the envelope's onBehalfOf attribution slot. */
|
|
41
|
+
as: "onBehalfOf";
|
|
42
|
+
/** true (DEFAULT) ⇒ onBehalfOf = "sha256:<hex(sha256(value))>", computed
|
|
43
|
+
* in-process. false = EXPLICIT owner opt-in to raw values. */
|
|
44
|
+
hash: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface RecognizerEntry {
|
|
47
|
+
/** Exact destination host (post-alias), e.g. "api.stripe.com". */
|
|
48
|
+
host: string;
|
|
49
|
+
/** Uppercase method. */
|
|
50
|
+
method: string;
|
|
51
|
+
/** Templated path this entry matches exactly, e.g. "/v1/charges/{id}/refunds". */
|
|
52
|
+
path: string;
|
|
53
|
+
/**
|
|
54
|
+
* JSON-RPC operation this entry matches (crypto rails). When set, the entry
|
|
55
|
+
* matches host + POST + the request body's JSON-RPC `method === rpcMethod`
|
|
56
|
+
* INSTEAD of the exact path — every RPC call POSTs to one path ("/") with the
|
|
57
|
+
* operation in the body, so path alone cannot tell `sendTransaction` from
|
|
58
|
+
* `getBalance`. Keyed separately (`${host}|rpc|${rpcMethod}`); entries WITHOUT
|
|
59
|
+
* rpcMethod keep matching by host+method+path exactly as before. */
|
|
60
|
+
rpcMethod?: string;
|
|
61
|
+
rail: string;
|
|
62
|
+
action: string;
|
|
63
|
+
/** SAFE request-body fields to recognize (dot paths). L2 denylist still applies. */
|
|
64
|
+
requestFields?: string[];
|
|
65
|
+
/** SAFE response-JSON fields to recognize (dot paths). L2 denylist still applies. */
|
|
66
|
+
responseFields?: string[];
|
|
67
|
+
/** Wanted identity → onBehalfOf. Exempt from the capture denylist ONLY in
|
|
68
|
+
* this slot; it can never appear in requestFields/responseFields (entries
|
|
69
|
+
* that try are rejected by validateManifest — belt and braces). */
|
|
70
|
+
identityField?: IdentityFieldConfig;
|
|
71
|
+
/** Escort (enforce) config — set only on promoted/escorted routes. */
|
|
72
|
+
escort?: EscortConfig;
|
|
73
|
+
}
|
|
74
|
+
/** A compiled, resolved escort (settleUrl + rail + action filled in). */
|
|
75
|
+
export interface CompiledEscort extends EscortConfig {
|
|
76
|
+
settleUrl: string;
|
|
77
|
+
rail: string;
|
|
78
|
+
action: string;
|
|
79
|
+
}
|
|
80
|
+
export interface RouteMatcher {
|
|
81
|
+
host: string;
|
|
82
|
+
method?: string;
|
|
83
|
+
/** Matches when the templated path starts with this prefix. */
|
|
84
|
+
pathPrefix?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface ObserveManifest {
|
|
87
|
+
/** Registry version stamped into rec.registryVersion. */
|
|
88
|
+
version: string;
|
|
89
|
+
/** Content-hashed policy version (stamped on events when present). */
|
|
90
|
+
policyVersion?: string;
|
|
91
|
+
recognizers: RecognizerEntry[];
|
|
92
|
+
/** Routes forced into exact per-minute counter aggregation. */
|
|
93
|
+
counters: RouteMatcher[];
|
|
94
|
+
/** Routes never reported at all (capture policy `mute`). */
|
|
95
|
+
mutes?: RouteMatcher[];
|
|
96
|
+
/** TODO: signed-manifest verification (see header comment). Ignored for now. */
|
|
97
|
+
signature?: string;
|
|
98
|
+
}
|
|
99
|
+
/** Built-in default manifest: metadata-only + curated safe-field recognizers.
|
|
100
|
+
* SAFE fields only: amounts (cents), opaque IDs, model names, token counts. */
|
|
101
|
+
export declare const BUILTIN_MANIFEST: ObserveManifest;
|
|
102
|
+
/** Env-derived escort pin parameters (register preload, BOUNDED_* env). Kept
|
|
103
|
+
* on ObserveConfig so the pin is re-applied over EVERY manifest swap — a
|
|
104
|
+
* fetched manifest can never silently drop the pinned enforcement. */
|
|
105
|
+
export interface EscortPin {
|
|
106
|
+
verdictUrl: string;
|
|
107
|
+
collection: string;
|
|
108
|
+
failMode: "closed" | "open";
|
|
109
|
+
timeoutMs?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Pin an escort block onto the Stripe-refund recognizer (api.stripe.com POST
|
|
113
|
+
* /v1/refunds). Everything else stays OBSERVE. Applied over the built-in at
|
|
114
|
+
* boot (register preload) AND re-applied over every fetched manifest swap.
|
|
115
|
+
* Idempotent, fetched-wins:
|
|
116
|
+
* - If the entry ALREADY carries an escort block (server-side promotion in a
|
|
117
|
+
* fetched manifest), that block wins and the env pin is skipped.
|
|
118
|
+
* - If the entry is MISSING (a fetched manifest dropped the route), it is
|
|
119
|
+
* restored from the built-in recognizer WITH the pin — enforcement armed
|
|
120
|
+
* via BOUNDED_VERDICT_URL can never be silently switched off by a swap.
|
|
121
|
+
* The "+escort" version suffix is appended only when the pin was applied, and
|
|
122
|
+
* never doubled. counters/mutes/policyVersion of the base are preserved.
|
|
123
|
+
*/
|
|
124
|
+
export declare function escortedManifest(base: ObserveManifest, verdictUrl: string, collection: string, failMode: "closed" | "open", timeoutMs?: number): ObserveManifest;
|
|
125
|
+
/** Apply an (optional) env escort pin over a manifest. No pin = passthrough. */
|
|
126
|
+
export declare function withEscortPin(base: ObserveManifest, pin: EscortPin | undefined): ObserveManifest;
|
|
127
|
+
/** Compiled manifest index for O(1)-ish hot-path decisions. */
|
|
128
|
+
export declare class CompiledManifest {
|
|
129
|
+
readonly manifest: ObserveManifest;
|
|
130
|
+
readonly source: "builtin" | "override" | "fetched";
|
|
131
|
+
/** host|METHOD|templatedPath -> entry (non-rpc entries only). */
|
|
132
|
+
private readonly exact;
|
|
133
|
+
/** host|rpc|rpcMethod -> entry (crypto JSON-RPC entries; matched on the
|
|
134
|
+
* request body's `method`, never the path). */
|
|
135
|
+
private readonly rpc;
|
|
136
|
+
/** Hosts carrying any rpcMethod recognizer (gates the JSON-RPC body parse). */
|
|
137
|
+
private readonly rpcHosts;
|
|
138
|
+
/** Hosts with any recognizer (request-body capture allowed). */
|
|
139
|
+
readonly captureHosts: Set<string>;
|
|
140
|
+
/** host|METHOD|templatedPath entries whose responseFields need a body read. */
|
|
141
|
+
private readonly respCapture;
|
|
142
|
+
/** host|METHOD|templatedPath -> compiled escort (non-GET/HEAD routes only). */
|
|
143
|
+
private readonly escorts;
|
|
144
|
+
/** True if ANY route is escorted — the hot-path gate for escort mode. When
|
|
145
|
+
* false (the observe-only case) the interceptor does zero extra work. */
|
|
146
|
+
readonly hasEscorts: boolean;
|
|
147
|
+
constructor(manifest: ObserveManifest, source: "builtin" | "override" | "fetched");
|
|
148
|
+
get version(): string;
|
|
149
|
+
recognize(host: string, method: string, pathTemplate: string): RecognizerEntry | undefined;
|
|
150
|
+
/** True if the host carries any crypto JSON-RPC recognizer — the cheap Set
|
|
151
|
+
* check that decides whether to parse the request body for its `method`. */
|
|
152
|
+
hasRpcHost(host: string): boolean;
|
|
153
|
+
/** Recognize a crypto JSON-RPC call by host + the body's `method`. */
|
|
154
|
+
recognizeRpc(host: string, rpcMethod: string): RecognizerEntry | undefined;
|
|
155
|
+
wantsResponseBody(host: string, method: string, pathTemplate: string): boolean;
|
|
156
|
+
/** Compiled escort for a route, or undefined. Reads are never escorted. */
|
|
157
|
+
escort(host: string, method: string, pathTemplate: string): CompiledEscort | undefined;
|
|
158
|
+
private matchList;
|
|
159
|
+
isCounterRoute(host: string, method: string, pathTemplate: string): boolean;
|
|
160
|
+
isMuted(host: string, method: string, pathTemplate: string): boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Structurally validate an untrusted manifest object. Returns a sanitized
|
|
164
|
+
* ObserveManifest or null (=> caller keeps the current manifest; fail-safe).
|
|
165
|
+
* L2: recognizer field lists are filtered through the hard PII denylist —
|
|
166
|
+
* a bad manifest cannot widen capture into PII-named fields.
|
|
167
|
+
*/
|
|
168
|
+
export declare function validateManifest(raw: unknown): ObserveManifest | null;
|