@blamejs/blamejs-shop 0.0.121 → 0.0.122
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/CHANGELOG.md +2 -0
- package/README.md +3 -1
- package/SECURITY.md +9 -0
- package/lib/admin.js +51 -0
- package/lib/order.js +23 -0
- package/lib/reviews.js +29 -0
- package/lib/storefront.js +359 -9
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +14 -0
- package/lib/vendor/blamejs/README.md +2 -0
- package/lib/vendor/blamejs/SECURITY.md +1 -0
- package/lib/vendor/blamejs/api-snapshot.json +195 -2
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/ai-capability.js +482 -0
- package/lib/vendor/blamejs/lib/ai-disclosure.js +107 -0
- package/lib/vendor/blamejs/lib/ai-quota.js +526 -0
- package/lib/vendor/blamejs/lib/backup/index.js +210 -1
- package/lib/vendor/blamejs/lib/compliance.js +48 -1
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.23.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.24.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.25.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.26.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.12.27.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.12.28.json +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-capability.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-disclosure-apply-all.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-quota.test.js +264 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-clone-bundle.test.js +178 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-find-bundles.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-rewrap-all.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eu-ai-act-posture.test.js +93 -0
- package/package.json +1 -1
|
@@ -8,6 +8,20 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.12.x
|
|
10
10
|
|
|
11
|
+
- v0.12.28 (2026-05-24) — **`b.ai.capability` — model-capability registry + cheapest-satisfying-model router.** `b.ai.capability.create({ models })` turns a fleet of AI model descriptors into a routing decision: given a set of requirements (context window, input/output modalities, tool use, structured output, reasoning tier, citation support, prompt-caching size), it picks the cheapest model that satisfies all of them. NIST AI RMF (AI 100-1) MAP 2.x requires documenting each model's capabilities and limitations; the Model Cards convention (Mitchell et al., 2019) formalizes that descriptor — this primitive makes the descriptor actionable. Routing to the cheapest sufficient model is a front-line defense against over-provisioning spend and composes directly with `b.ai.quota`'s `cost-usd` dimension (the chosen descriptor's rate feeds the budget charge); refusing to route a request to a model that cannot satisfy it (missing modality, too-small context window, no tool use) catches a capability mismatch before the inference call burns tokens on a guaranteed-bad result. Cost ranking uses a supplied `costBasis` (`{ inputTokens, outputTokens }`) for real per-call spend, else the sum of the per-1k rates; ties break by model id so the choice is deterministic across calls and nodes. **Added:** *`b.ai.capability.create({ models })` — capability registry + router* — Returns `{ describe, list, register, satisfies, route }`. A descriptor carries `maxContextTokens`, `maxOutputTokens`, `modalitiesIn` / `modalitiesOut` (arrays), `toolUse`, `structuredOutput`, `fineTunable`, `reasoningTier` (`none` / `basic` / `standard` / `advanced`, ordered), `citationSupport`, `promptCachingMaxTokens`, and the cost rates `costPer1kInputTokens` / `costPer1kOutputTokens`. Descriptors are validated + frozen at registration so a typo (negative cost, unknown reasoning tier, non-array modality list) surfaces at config time rather than as a silent mis-route. `describe(modelId)` returns the frozen descriptor; `register(modelId, descriptor)` adds or replaces one at runtime. · *`route({ requirements, fallback?, costBasis? })` — cheapest-satisfying selection* — Collects every model whose descriptor satisfies all requirements, then returns the cheapest (`{ modelId, descriptor, estimatedCost, reason }`). Requirements: `minContextTokens`, `minOutputTokens`, `modalitiesIn` / `modalitiesOut` (model must support every listed modality), `toolUse`, `structuredOutput`, `fineTunable`, `minReasoningTier` (tier ordering — `standard` is met by `standard` or `advanced`), `citationSupport`, `minPromptCachingTokens`. When no model matches, `fallback` (a registered model id) is returned with `reason: "fallback"`, or the call refuses with `aiCapability/no-candidate` if no fallback was supplied. Routing decisions emit `ai/capability-routed` / `ai/capability-fallback` / `ai/capability-no-candidate` through the drop-silent audit chain. · *`satisfies(modelId, requirements)` — precise capability-mismatch reasons* — Returns `{ ok, failures }` where each failure names the `requirement`, the `need`, and what the model `have`s — so a caller surfaces a precise reason (e.g. `minReasoningTier need advanced have basic`) instead of a bare boolean. Use it to explain a routing miss or to gate a request against a specific model before calling it.
|
|
12
|
+
|
|
13
|
+
- v0.12.27 (2026-05-24) — **`b.ai.quota` — per-tenant, per-model AI usage budgets with atomic consume-and-check.** `b.ai.quota.create(opts)` builds an enforcer that caps AI inference usage per `(tenant, model, dimension, period)` and defends OWASP LLM Top 10 2025 LLM10 (Unbounded Consumption) — the class that includes denial-of-wallet, where an attacker drives a high volume of pay-per-use inferences until the bill itself is the attack. Meter by `tokens`, `requests`, `cost-usd`, or `compute-hours` over a calendar-aligned UTC window (`second` through `month`). `consume(tenant, model, amount)` is a single atomic check-and-charge: under the default `hard` enforcement it reserves the amount only if it fits under the ceiling, otherwise it refuses without charging — the limit test and the charge are one indivisible operation, so there is no charge-then-refund window for a concurrent call to observe. The in-memory counter is per-process; multi-node deployments supply an `opts.store` adapter whose `reserve` (an atomic conditional test-and-charge — a Redis Lua script, a SQL `UPDATE ... WHERE used + :amt <= :limit RETURNING used`) and `add` are atomic on the shared backend to enforce one aggregate ceiling across the cluster without false denials under contention. Limit resolution is most-specific-first: `perTenantModel` over `perTenant` over `perModel` over the default `limit`; tenant and model identifiers are percent-encoded into the counter key so a hostile tenant name cannot collide with another tenant's budget. **Added:** *`b.ai.quota.create(opts)` — per-tenant AI usage-budget enforcer* — Returns `{ consume, check, snapshot, reset }` scoped to one `dimension` (`tokens` / `requests` / `cost-usd` / `compute-hours`) and one `period` (`second` / `minute` / `hour` / `day` / `week` (Monday-aligned) / `month` (1st-of-month), all UTC-aligned). `consume(tenant, model, amount, opts?)` returns `{ used, limit, remaining, allowed, exceeded, windowStart, resetsAt, ... }`. `check(tenant, model)` is the read-only snapshot. Spin up one enforcer per dimension you meter — a monthly `cost-usd` budget and a per-minute `tokens` burst cap coexist as two `create()` calls sharing one store. Defends OWASP LLM10:2025 Unbounded Consumption / denial-of-wallet; maps to NIST AI RMF (AI 100-1) MANAGE 2.x and EU AI Act Art. 15 (robustness / resource-exhaustion resilience). · *`hard` / `soft` / `warn` enforcement* — `hard` (default) refuses the over-budget call and throws `aiQuota/exceeded` without charging — the rejected reservation is refunded so the counter is untouched. `soft` admits the charge but reports `allowed: false` so the caller decides whether to honor it. `warn` admits and allows (advisory), flagging `exceeded: true`. A per-call `consume(..., { enforcement })` override lets one endpoint soften the mode for a trusted internal caller without a second enforcer. Every over-budget event emits `ai/quota-exceeded` through the drop-silent audit chain (`ai/quota-applied` on success), tagged with the active cluster node id for attribution. · *Cross-node aggregate budgets via `opts.store`* — The default counter is in-memory (per-process). Supply `opts.store` exposing atomic `reserve` / `add` / `get` / `reset` (a Redis Lua script, a shared SQL row) and the ceiling is enforced on the cluster-wide aggregate. `hard` mode goes through `reserve`, an atomic conditional test-and-charge that adds the amount only if it fits — so a concurrent over-budget call cannot transiently inflate the counter and falsely deny a smaller call that should fit. Per-tenant and per-model limit overrides (`perTenant` / `perModel` / `perTenantModel`) are validated at config time so a malformed cap surfaces at boot, not as a silent fall-through to the default.
|
|
14
|
+
|
|
15
|
+
- v0.12.26 (2026-05-24) — **`b.compliance` posture cascades — `eu-ai-act` + `ca-ab-853` + `cac-genai-label` POSTURE_DEFAULTS + backup encryption refusal.** Three new posture cascades wired into `b.compliance.POSTURE_DEFAULTS` + `KNOWN_POSTURES` + `REGIME_MAP` so operators globally pinning the EU AI Act / California AB-853 / China CAC GenAI postures get the right floors automatically: backupEncryptionRequired:true, auditChainSignedRequired:true, tlsMinVersion:TLSv1.3, requireVacuumAfterErase:true. `b.backup.bundleAdapterStorage` extends the encryption-required posture list to include the three new postures so `cryptoStrategy: "none"` is refused upfront under any of them (parity with HIPAA + PCI-DSS, which the operator surface has carried since v0.12.10). The canonical `eu-ai-act` posture is the production name; the legacy `ai-act` short name stays in KNOWN_POSTURES for back-compat with operators who pinned it pre-v0.12.26. **Added:** *`eu-ai-act` posture cascade — Regulation (EU) 2024/1689* — POSTURE_DEFAULTS entry: backupEncryptionRequired:true (Art. 12 logging + Art. 15 robustness/cybersecurity demand encryption-at-rest for high-risk system training logs), auditChainSignedRequired:true (Art. 12 + Art. 13 audit-chain integrity), tlsMinVersion:TLSv1.3, requireVacuumAfterErase:true (Art. 50(4) synthetic-content provenance — residual EXIF / metadata pointing at the generating model must be cleared on erase). REGIME_MAP entry under jurisdiction:"EU" domain:"ai-governance". KNOWN_POSTURES carries both `eu-ai-act` (canonical) and `ai-act` (legacy short name). · *`ca-ab-853` posture cascade — California AB-853 effective 2026* — Same encryption + audit floor as eu-ai-act; jurisdiction:"US-CA". Model-generated content watermarking + disclosure regime. Operators serving California traffic pin this posture for the AB-853 §22949.91 obligations the v0.12.12 deepfake primitive's crossWalk references. · *`cac-genai-label` posture cascade — China CAC GenAI Service Measures* — Synthetic-content labelling per Art. 12 + algorithm filing per Art. 4. Same backup encryption + signed audit chain floor. Operators serving Chinese traffic pin this posture so the bundleAdapterStorage refuses plaintext bundles and the disclosure primitive's `jurisdiction: "cn"` cross-walk produces the right legal-reference array. · *`bundleAdapterStorage` BACKUP_ENCRYPTION_REQUIRED_POSTURES extended* — `hipaa` + `pci-dss` (the v0.12.10 baseline) joined by the three AI postures. `cryptoStrategy: "none"` refused upfront under any of `eu-ai-act` / `ca-ab-853` / `cac-genai-label` with `backup/posture-requires-encryption`. Operators wiring backup storage in a regulated AI deployment now get the same posture-driven gate that the storage primitive has always applied to health + payment data.
|
|
16
|
+
|
|
17
|
+
- v0.12.25 (2026-05-24) — **`b.ai.disclosure.applyAll(scenario)` — bundle Art. 50(1) / 50(3) / 50(4) disclosures for mixed-modality AI systems.** Composes the three v0.12.12 disclosure primitives (chatbot / deepfake / emotion) into a single bundled emit. Operators running mixed-modality AI systems (e.g. a chatbot that also generates images, or an emotion-recognition system embedded in a chat flow) declare which Art. 50 obligations apply via `scenario.kinds` and the primitive fans out to the per-obligation emit calls in one pass. Shared opts (jurisdiction, language, audit, correlationId) propagate to every per-kind emission so the cross-walk + audit-chain entries stay correlated across the bundle. **Added:** *`b.ai.disclosure.applyAll(scenario)` — multi-obligation bundled emit* — `scenario.kinds: ["chatbot", "deepfake", "emotion"]` (subset) selects which Art. 50 obligations to satisfy. Per-kind required fields (session for chatbot, content + contentType for deepfake) refused upfront when missing. Returns `{ disclosures: { chatbot?, deepfake?, emotion? } }` with each entry being the corresponding primitive's emission payload. Shared opts propagate: `scenario.jurisdiction` / `scenario.language` / `scenario.audit` / `scenario.correlationId` reach every per-kind call so a US-CA deployment serving chat + image gets both the AB-853 cross-walk AND the Art. 50(1) audit event under the same correlationId.
|
|
18
|
+
|
|
19
|
+
- v0.12.24 (2026-05-24) — **`bundleAdapterStorage.findBundles(predicate, opts?)` — predicate-based filtering over listBundles entries.** Small helper that composes with listBundles for operators wanting to filter the bundle set without hand-rolling the walk. `storage.findBundles(predicate, opts?)` iterates listBundles + returns entries where `predicate(entry)` is truthy. Predicate sees the listBundles entry shape (`{ bundleId, format, createdAt, size }`); `opts.withStats: true` enables `createdAt` + `size` for predicates that need them. Common operator filters — by format (`b => b.format === "tar.gz"`), by age (`b => Date.parse(b.createdAt) < cutoff`), by size — now read as a single call. **Added:** *`storage.findBundles(predicate, opts?)` — predicate-based bundle filter* — Operator-supplied predicate runs against every listBundles entry; matches accumulate into the returned array. `opts.withStats: true` is forwarded to listBundles so predicates relying on `createdAt` / `size` see populated values. Non-function predicate refused upfront with `backup/bad-arg`. Predicate throws bubble up to the caller (operators see their own filter errors, not swallowed). Stable ordering is whatever listBundles produces (reverse-chronological by bundleId).
|
|
20
|
+
|
|
21
|
+
- v0.12.23 (2026-05-24) — **`bundleAdapterStorage.cloneBundle(src, dst, opts?)` — same-storage byte-verbatim bundle clone for pre-rotation snapshots.** `storage.cloneBundle(srcBundleId, dstBundleId, opts?)` copies a bundle's adapter payload (bundle.tar / bundle.tar.gz / every directory key) from src to dst WITHOUT touching the envelope or inner archive. Encrypted bundles are cloned byte-verbatim — the new bundleId carries the same envelope under the same recipient/passphrase. Operators preserving a known-good snapshot before a destructive operation (rewrap, key rotation, schema migration, manual operator-side editing) get a single-call atomic clone instead of a manual readBundle → writeBundle cycle (which would re-encode through the envelope and adapter contracts, breaking byte-identity). **Added:** *`storage.cloneBundle(src, dst, opts?)` — byte-verbatim payload clone* — Reads the source bundle's storage keys + writes them under the destination bundleId without invoking the wrap layer, gunzip path, or tar walker. Encrypted bundles produce byte-identical clones (a tar.gz wrap-recipient envelope cloned via cloneBundle has bit-for-bit equal bytes to the source). Returns `{ srcBundleId, dstBundleId, format, keysCopied, bytesCopied }`. `opts.overwrite` (default false) gates whether to refuse if dstBundleId already exists. Same-id clones refused upfront with `backup/clone-same-id`.
|
|
22
|
+
|
|
23
|
+
- v0.12.22 (2026-05-24) — **`bundleAdapterStorage.rewrapAllBundles(opts)` — bounded-parallel batch envelope rotation with mixed-storage skip semantics.** Batch wrapper over the v0.12.21 rewrapBundle primitive. `storage.rewrapAllBundles(opts?)` iterates `listBundles()` + rotates each bundle's wrap envelope through a bounded-parallel pool (default 4 workers). Plaintext bundles + directory-format bundles get skipped cleanly (recorded as `status: "skipped"` with a `reason` field); rewrap failures get bucketed into `status: "failed"`. Operators completing a key-rotation event across an entire backup repository now have a single call that handles mixed-strategy storage correctly. `opts.newRecipient` / `opts.newPassphrase` / `opts.oldRecipient` / `opts.oldPassphrase` / `opts.concurrency` / `opts.stopOnFirstFailure` mirror the verifyAllBundles + rewrapBundle surface. **Added:** *`storage.rewrapAllBundles(opts?)` — batch envelope rotation* — Iterates listBundles() + dispatches each bundle through rewrapBundle with the operator-supplied new key. Returns `{ total, rotated, skipped, failed, results }` where the per-bundle results carry `{ status: "rotated" | "skipped" | "failed", oldEnvelopeKind, newEnvelopeKind, reason }`. Bounded-parallel fan-out (default 4) keeps the storage backend under control; opts.stopOnFirstFailure short-circuits on the first rotation that throws an unexpected error (skips don't trip the short-circuit — they're expected for mixed-strategy storage). Plaintext + directory bundles skipped with `reason: "format-not-wrappable"` / `reason: "no-envelope"` rather than reported as failures.
|
|
24
|
+
|
|
11
25
|
- v0.12.21 (2026-05-24) — **`bundleAdapterStorage.rewrapBundle(bundleId, opts)` — key rotation without restore + rewrite of inner archive bytes.** `storage.rewrapBundle(bundleId, opts?)` rotates a bundle's wrap envelope under a new recipient keypair or passphrase WITHOUT touching the inner tar / tar.gz bytes. Operators rotating a compromised keypair, migrating to a new HSM, or refreshing passphrases on a HIPAA-posture repository previously had to `readBundle` → write to a stage dir → `writeBundle` under the new key — three byte-walks of the bundle payload, two filesystem touches, transient plaintext on disk. rewrapBundle does it as unwrap + rewrap in memory: zero disk plaintext, one round-trip through the wrap layer, the gzipped tar archive bytes inside the envelope are never inflated. Cross-kind rotation (recipient ↔ passphrase) is refused — that's a separate migration the operator configures with explicit cryptoStrategy switch. **Added:** *`storage.rewrapBundle(bundleId, opts?)` — in-place envelope rotation* — Unwraps the bundle under the old key (storage's configured recipient/passphrase OR `opts.oldRecipient` / `opts.oldPassphrase`), re-wraps under the new key (`opts.newRecipient` / `opts.newPassphrase`), writes the rewrapped bytes back to the same storage key. Returns `{ bundleId, oldEnvelopeKind, newEnvelopeKind, bytesRewritten }`. Plaintext bundles refused with `backup/no-envelope-to-rewrap`; cross-kind rotation refused with `backup/no-new-recipient` / `backup/no-new-passphrase`. The inner archive bytes (the gz-compressed tar payload) are never decompressed or re-encoded — rewrap is a wrap-layer-only operation. **Security:** *Zero plaintext on disk during rotation* — The inner tar bytes flow only through memory — old-envelope unwrap → new-envelope wrap → adapter writeFile. Operators previously rotating via readBundle + writeBundle wrote plaintext archive bytes to a temporary stage directory; rewrapBundle removes that exposure window entirely. Matches the operator-side discipline that backup payloads should never land on disk in plaintext form during steady-state operations.
|
|
12
26
|
|
|
13
27
|
- v0.12.20 (2026-05-24) — **`bundleAdapterStorage.verifyAllBundles(opts)` — bounded-parallel batch integrity walk with stopOnFirstFailure short-circuit.** Batch wrapper over the v0.12.19 verifyBundle primitive. `storage.verifyAllBundles(opts?)` iterates `listBundles()` + walks each bundle with a bounded-parallel pool. Returns `{ total, ok, failed, results }` where `results` carries every per-bundle verifyBundle output (including bundleId, format, envelopeKind, entryCount, errors). `opts.concurrency` defaults to 4 (gentle on the storage backend); `opts.stopOnFirstFailure` short-circuits the walk when an unhealthy bundle is found (default off — operators want the full health report). `opts.recipient` / `opts.passphrase` forwarded to every per-bundle verify call. Operators wiring a periodic cron job over a backup repository now have a single primitive to call instead of hand-rolling the listBundles loop. **Added:** *`storage.verifyAllBundles(opts?)` — batch integrity walk* — Iterates `listBundles()` + calls `verifyBundle(bundleId, opts)` on each. Bounded-parallel fan-out (default 4 workers; `opts.concurrency` raises or lowers); each worker pulls from a shared queue + the warm-up keeps `concurrency` workers in flight until the queue drains. Returns the aggregate `{ total, ok, failed, results }` with `results` sorted by bundleId so the report is stable across runs regardless of completion order. `opts.stopOnFirstFailure` short-circuits the walk when the first unhealthy bundle is found — useful for fast-fail CI gates that don't need to enumerate every failure.
|
|
@@ -162,6 +162,8 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
|
|
|
162
162
|
- **Prompt-injection classification** — OWASP LLM01:2025 / NIST COSAIS RFI (`b.ai.input.classify`)
|
|
163
163
|
- **Agent identity** — A2A signed agent-card primitive (Linux Foundation Agentic AI Foundation v1.x, ML-DSA-87) (`b.a2a`)
|
|
164
164
|
- **Content provenance** — C2PA 2.1 + California SB-942 / AB-853 manifest builder for AI-generated media (provider, model id + version, timestamp, content ID, signed) (`b.contentCredentials`)
|
|
165
|
+
- **AI usage quotas** — per-tenant / per-model budgets metered by tokens / requests / cost-usd / compute-hours over calendar-aligned windows, with an atomic conditional reserve (no charge-then-refund race) + hard/soft/warn enforcement and an optional cross-node store; defends OWASP LLM10:2025 unbounded consumption / denial-of-wallet (`b.ai.quota`)
|
|
166
|
+
- **AI capability routing** — model-capability registry (context window / modalities / tool use / reasoning tier / cost rates) + a router that picks the cheapest model satisfying a request's requirements, refusing capability mismatches before the inference call (NIST AI RMF MAP + Model Cards); composes with `b.ai.quota` cost budgets (`b.ai.capability`)
|
|
165
167
|
### Compliance regimes
|
|
166
168
|
|
|
167
169
|
- **Posture coordinator** — `b.compliance` cascades operator-declared regime into retention / audit / db / cryptoField via POSTURE_DEFAULTS:
|
|
@@ -341,6 +341,7 @@ This is the minimum-viable security posture for a production deployment. The fra
|
|
|
341
341
|
- [ ] **Default-on (v0.7.12+):** `b.fileUpload` and `b.staticServe` wire `b.guardAll.byExtension({ profile: "strict" })` automatically; `b.fileUpload` additionally wires `b.guardFilename.gate({ profile: "strict" })` as `filenameSafety`. No explicit operator action required for the baseline defense-in-depth. To opt up to a broader content vocabulary (e.g. you serve operator-built HTML with links + images), pass `contentSafety: b.guardAll.byExtension({ profile: "balanced" })` explicitly. To opt out entirely (test fixtures, raw-bytes uploads), pass `contentSafety: null` / `filenameSafety: null` with `contentSafetyDisabledReason` / `filenameSafetyDisabledReason` strings — both fire audit rows at create() time so a security review can reconstruct which deploys disabled the default-on protection. To skip a single guard while keeping the rest, pass `contentSafety: b.guardAll.byExtension({ exceptFor: { name: { reason: "..." } } })` — the reason lands in the `guardAll.gate.created` audit row. Future guards added to the family auto-extend the deploy without re-wiring
|
|
342
342
|
- [ ] For OAuth / OIDC RP callbacks: call `b.auth.oauth.parseCallback(query, opts?)` BEFORE consuming `code` — validates RFC 9207 AS Issuer Identifier (refuses iss-mismatch + OP `error=` redirects + state-mismatch CSRF). For FAPI 2.0 deployments add `b.fapi2.assertCallback(query)` (refuses missing iss; refuses bare-param under `fapi-2.0-message-signing` posture, requiring JARM `response`) and `b.fapi2.assertAuthzRequest(authzParams)` BEFORE issuing the redirect (refuses non-JAR authorization requests). For refresh-token flows, pass `seen({ jti, iss })` to `b.auth.oauth.refreshAccessToken` so reuse of an already-rotated token refuses BEFORE the HTTP exchange (RFC 9700 §4.13 / OAuth 2.1 §6.1)
|
|
343
343
|
- [ ] For Model Context Protocol servers exposing tools to LLM agents: wire `b.mcp.toolResult.sanitize(result, { posture: "refuse" })` over EVERY tool output before returning it to the model — defends OWASP LLM07 (sensitive tool output / prompt-injection echo back into the agent loop), refuses dangerous-HTML + off-allowlist URLs. Wrap each tool's input handler with `b.mcp.validateToolInput(toolName, input, schema)` (JSON Schema 2020-12 subset — `type` / `properties` / `required` / `enum` / `const` / length + range caps) so an LLM-supplied argument shape that doesn't match refuses BEFORE the tool runs. Define each tool's required scopes via `b.mcp.capability.create(scopes)` and gate execution on `cap.satisfiedBy(grantedScopes)` (LLM08 least-privilege)
|
|
344
|
+
- [ ] For AI inference endpoints (especially pay-per-use provider models): wire `b.ai.quota.create({ dimension, period, limit, enforcement: "hard" })` and call `quota.consume(tenant, model, amount)` BEFORE every inference — caps tokens / requests / cost-usd / compute-hours per tenant per window so one tenant cannot run up an unbounded bill (OWASP LLM10:2025 unbounded consumption / denial-of-wallet). For multi-node deployments supply an `opts.store` whose `reserve` (atomic conditional test-and-charge) + `add` are atomic on the shared backend (Redis Lua / a SQL `UPDATE ... WHERE used + :amt <= :limit`) so the ceiling is enforced on the cluster-wide aggregate, not per-process
|
|
344
345
|
- [ ] For data with a TTL (GDPR Art. 17, PCI 3.1, retention windows): declare retention rules via `b.retention.create({ db, audit }).declare({ name, table, ageField, ttlMs, action: "erase" })` and run on a `b.scheduler` cadence; honour legal-hold via `legalHoldField`
|
|
345
346
|
- [ ] For write-once-read-many object archives (SEC 17a-4, FINRA, HIPAA-shaped retention): create the bucket with `b.objectStore.bucketOps.create(name, { objectLockEnabled: true })` (Object Lock can ONLY be flipped at create time), apply a default retention via `setObjectLockConfiguration(name, { mode: "COMPLIANCE", years })`, and pin individual objects with `setObjectRetention(name, key, { mode, retainUntil })` or `setObjectLegalHold(name, key, "ON")` — `COMPLIANCE` cannot be shortened or bypassed by anyone (including root); pick deliberately
|
|
346
347
|
- [ ] At boot, before any outbound socket opens: call `b.network.bootFromEnv({ env: process.env, audit: b.audit })` so operator-supplied NTP / DNS / proxy / DPI-trust / TCP socket settings (`BLAMEJS_NTP_*`, `BLAMEJS_DNS_*`, `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`, `BLAMEJS_EXTRA_CA_CERTS`, `BLAMEJS_SOCKET_*`) apply uniformly
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"frameworkVersion": "0.12.
|
|
4
|
-
"createdAt": "2026-05-
|
|
3
|
+
"frameworkVersion": "0.12.28",
|
|
4
|
+
"createdAt": "2026-05-24T15:47:42.211Z",
|
|
5
5
|
"exports": {
|
|
6
6
|
"a2a": {
|
|
7
7
|
"type": "object",
|
|
@@ -1498,6 +1498,23 @@
|
|
|
1498
1498
|
}
|
|
1499
1499
|
}
|
|
1500
1500
|
},
|
|
1501
|
+
"capability": {
|
|
1502
|
+
"type": "object",
|
|
1503
|
+
"members": {
|
|
1504
|
+
"AiCapabilityError": {
|
|
1505
|
+
"type": "function",
|
|
1506
|
+
"arity": 4
|
|
1507
|
+
},
|
|
1508
|
+
"REASONING_TIERS": {
|
|
1509
|
+
"type": "instance",
|
|
1510
|
+
"ctorName": "Array"
|
|
1511
|
+
},
|
|
1512
|
+
"create": {
|
|
1513
|
+
"type": "function",
|
|
1514
|
+
"arity": 1
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
},
|
|
1501
1518
|
"disclosure": {
|
|
1502
1519
|
"type": "object",
|
|
1503
1520
|
"members": {
|
|
@@ -1513,6 +1530,10 @@
|
|
|
1513
1530
|
"type": "instance",
|
|
1514
1531
|
"ctorName": "Array"
|
|
1515
1532
|
},
|
|
1533
|
+
"applyAll": {
|
|
1534
|
+
"type": "function",
|
|
1535
|
+
"arity": 1
|
|
1536
|
+
},
|
|
1516
1537
|
"chatbot": {
|
|
1517
1538
|
"type": "function",
|
|
1518
1539
|
"arity": 2
|
|
@@ -1572,6 +1593,31 @@
|
|
|
1572
1593
|
"arity": 3
|
|
1573
1594
|
}
|
|
1574
1595
|
}
|
|
1596
|
+
},
|
|
1597
|
+
"quota": {
|
|
1598
|
+
"type": "object",
|
|
1599
|
+
"members": {
|
|
1600
|
+
"AiQuotaError": {
|
|
1601
|
+
"type": "function",
|
|
1602
|
+
"arity": 4
|
|
1603
|
+
},
|
|
1604
|
+
"DIMENSIONS": {
|
|
1605
|
+
"type": "instance",
|
|
1606
|
+
"ctorName": "Array"
|
|
1607
|
+
},
|
|
1608
|
+
"ENFORCEMENTS": {
|
|
1609
|
+
"type": "instance",
|
|
1610
|
+
"ctorName": "Array"
|
|
1611
|
+
},
|
|
1612
|
+
"PERIODS": {
|
|
1613
|
+
"type": "instance",
|
|
1614
|
+
"ctorName": "Array"
|
|
1615
|
+
},
|
|
1616
|
+
"create": {
|
|
1617
|
+
"type": "function",
|
|
1618
|
+
"arity": 1
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1575
1621
|
}
|
|
1576
1622
|
}
|
|
1577
1623
|
},
|
|
@@ -6360,6 +6406,27 @@
|
|
|
6360
6406
|
}
|
|
6361
6407
|
}
|
|
6362
6408
|
},
|
|
6409
|
+
"ai-act": {
|
|
6410
|
+
"type": "object",
|
|
6411
|
+
"members": {
|
|
6412
|
+
"auditChainSignedRequired": {
|
|
6413
|
+
"type": "primitive",
|
|
6414
|
+
"valueType": "boolean"
|
|
6415
|
+
},
|
|
6416
|
+
"backupEncryptionRequired": {
|
|
6417
|
+
"type": "primitive",
|
|
6418
|
+
"valueType": "boolean"
|
|
6419
|
+
},
|
|
6420
|
+
"requireVacuumAfterErase": {
|
|
6421
|
+
"type": "primitive",
|
|
6422
|
+
"valueType": "boolean"
|
|
6423
|
+
},
|
|
6424
|
+
"tlsMinVersion": {
|
|
6425
|
+
"type": "primitive",
|
|
6426
|
+
"valueType": "string"
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
},
|
|
6363
6430
|
"ar-pdpa": {
|
|
6364
6431
|
"type": "object",
|
|
6365
6432
|
"members": {
|
|
@@ -6402,6 +6469,27 @@
|
|
|
6402
6469
|
}
|
|
6403
6470
|
}
|
|
6404
6471
|
},
|
|
6472
|
+
"ca-ab-853": {
|
|
6473
|
+
"type": "object",
|
|
6474
|
+
"members": {
|
|
6475
|
+
"auditChainSignedRequired": {
|
|
6476
|
+
"type": "primitive",
|
|
6477
|
+
"valueType": "boolean"
|
|
6478
|
+
},
|
|
6479
|
+
"backupEncryptionRequired": {
|
|
6480
|
+
"type": "primitive",
|
|
6481
|
+
"valueType": "boolean"
|
|
6482
|
+
},
|
|
6483
|
+
"requireVacuumAfterErase": {
|
|
6484
|
+
"type": "primitive",
|
|
6485
|
+
"valueType": "boolean"
|
|
6486
|
+
},
|
|
6487
|
+
"tlsMinVersion": {
|
|
6488
|
+
"type": "primitive",
|
|
6489
|
+
"valueType": "string"
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
},
|
|
6405
6493
|
"ca-ab853": {
|
|
6406
6494
|
"type": "object",
|
|
6407
6495
|
"members": {
|
|
@@ -6465,6 +6553,27 @@
|
|
|
6465
6553
|
}
|
|
6466
6554
|
}
|
|
6467
6555
|
},
|
|
6556
|
+
"cac-genai-label": {
|
|
6557
|
+
"type": "object",
|
|
6558
|
+
"members": {
|
|
6559
|
+
"auditChainSignedRequired": {
|
|
6560
|
+
"type": "primitive",
|
|
6561
|
+
"valueType": "boolean"
|
|
6562
|
+
},
|
|
6563
|
+
"backupEncryptionRequired": {
|
|
6564
|
+
"type": "primitive",
|
|
6565
|
+
"valueType": "boolean"
|
|
6566
|
+
},
|
|
6567
|
+
"requireVacuumAfterErase": {
|
|
6568
|
+
"type": "primitive",
|
|
6569
|
+
"valueType": "boolean"
|
|
6570
|
+
},
|
|
6571
|
+
"tlsMinVersion": {
|
|
6572
|
+
"type": "primitive",
|
|
6573
|
+
"valueType": "string"
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
},
|
|
6468
6577
|
"can-spam": {
|
|
6469
6578
|
"type": "object",
|
|
6470
6579
|
"members": {
|
|
@@ -6973,6 +7082,27 @@
|
|
|
6973
7082
|
}
|
|
6974
7083
|
}
|
|
6975
7084
|
},
|
|
7085
|
+
"eu-ai-act": {
|
|
7086
|
+
"type": "object",
|
|
7087
|
+
"members": {
|
|
7088
|
+
"auditChainSignedRequired": {
|
|
7089
|
+
"type": "primitive",
|
|
7090
|
+
"valueType": "boolean"
|
|
7091
|
+
},
|
|
7092
|
+
"backupEncryptionRequired": {
|
|
7093
|
+
"type": "primitive",
|
|
7094
|
+
"valueType": "boolean"
|
|
7095
|
+
},
|
|
7096
|
+
"requireVacuumAfterErase": {
|
|
7097
|
+
"type": "primitive",
|
|
7098
|
+
"valueType": "boolean"
|
|
7099
|
+
},
|
|
7100
|
+
"tlsMinVersion": {
|
|
7101
|
+
"type": "primitive",
|
|
7102
|
+
"valueType": "string"
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
},
|
|
6976
7106
|
"eu-cer": {
|
|
6977
7107
|
"type": "object",
|
|
6978
7108
|
"members": {
|
|
@@ -8918,6 +9048,27 @@
|
|
|
8918
9048
|
}
|
|
8919
9049
|
}
|
|
8920
9050
|
},
|
|
9051
|
+
"ca-ab-853": {
|
|
9052
|
+
"type": "object",
|
|
9053
|
+
"members": {
|
|
9054
|
+
"citation": {
|
|
9055
|
+
"type": "primitive",
|
|
9056
|
+
"valueType": "string"
|
|
9057
|
+
},
|
|
9058
|
+
"domain": {
|
|
9059
|
+
"type": "primitive",
|
|
9060
|
+
"valueType": "string"
|
|
9061
|
+
},
|
|
9062
|
+
"jurisdiction": {
|
|
9063
|
+
"type": "primitive",
|
|
9064
|
+
"valueType": "string"
|
|
9065
|
+
},
|
|
9066
|
+
"name": {
|
|
9067
|
+
"type": "primitive",
|
|
9068
|
+
"valueType": "string"
|
|
9069
|
+
}
|
|
9070
|
+
}
|
|
9071
|
+
},
|
|
8921
9072
|
"ca-ab853": {
|
|
8922
9073
|
"type": "object",
|
|
8923
9074
|
"members": {
|
|
@@ -9002,6 +9153,27 @@
|
|
|
9002
9153
|
}
|
|
9003
9154
|
}
|
|
9004
9155
|
},
|
|
9156
|
+
"cac-genai-label": {
|
|
9157
|
+
"type": "object",
|
|
9158
|
+
"members": {
|
|
9159
|
+
"citation": {
|
|
9160
|
+
"type": "primitive",
|
|
9161
|
+
"valueType": "string"
|
|
9162
|
+
},
|
|
9163
|
+
"domain": {
|
|
9164
|
+
"type": "primitive",
|
|
9165
|
+
"valueType": "string"
|
|
9166
|
+
},
|
|
9167
|
+
"jurisdiction": {
|
|
9168
|
+
"type": "primitive",
|
|
9169
|
+
"valueType": "string"
|
|
9170
|
+
},
|
|
9171
|
+
"name": {
|
|
9172
|
+
"type": "primitive",
|
|
9173
|
+
"valueType": "string"
|
|
9174
|
+
}
|
|
9175
|
+
}
|
|
9176
|
+
},
|
|
9005
9177
|
"can-spam": {
|
|
9006
9178
|
"type": "object",
|
|
9007
9179
|
"members": {
|
|
@@ -9674,6 +9846,27 @@
|
|
|
9674
9846
|
}
|
|
9675
9847
|
}
|
|
9676
9848
|
},
|
|
9849
|
+
"eu-ai-act": {
|
|
9850
|
+
"type": "object",
|
|
9851
|
+
"members": {
|
|
9852
|
+
"citation": {
|
|
9853
|
+
"type": "primitive",
|
|
9854
|
+
"valueType": "string"
|
|
9855
|
+
},
|
|
9856
|
+
"domain": {
|
|
9857
|
+
"type": "primitive",
|
|
9858
|
+
"valueType": "string"
|
|
9859
|
+
},
|
|
9860
|
+
"jurisdiction": {
|
|
9861
|
+
"type": "primitive",
|
|
9862
|
+
"valueType": "string"
|
|
9863
|
+
},
|
|
9864
|
+
"name": {
|
|
9865
|
+
"type": "primitive",
|
|
9866
|
+
"valueType": "string"
|
|
9867
|
+
}
|
|
9868
|
+
}
|
|
9869
|
+
},
|
|
9677
9870
|
"eu-cer": {
|
|
9678
9871
|
"type": "object",
|
|
9679
9872
|
"members": {
|
|
@@ -443,6 +443,8 @@ module.exports = {
|
|
|
443
443
|
aiContentDetect: require("./lib/ai-content-detect"),
|
|
444
444
|
modelManifest: require("./lib/ai-model-manifest"),
|
|
445
445
|
disclosure: require("./lib/ai-disclosure"),
|
|
446
|
+
quota: require("./lib/ai-quota"),
|
|
447
|
+
capability: require("./lib/ai-capability"),
|
|
446
448
|
},
|
|
447
449
|
promisePool: require("./lib/promise-pool"),
|
|
448
450
|
sdNotify: require("./lib/sd-notify"),
|