Gate Contract

Shared substrate every b.guard* primitive composes against — resolveProfileAndPosture, makeProfileBuilder, makeRulePackLoader, lookupCompliancePosture, buildGuardGate, aggregateIssues, extractBytesAsText. The contract every guard implements; ensures the action vocabulary (serve / sanitize / refuse / audit-only) and the profile-and-posture resolution shape stay identical across the family.

Every guard ships a .gate(opts) factory returning the shape defined here. Host primitives (b.staticServe / b.fileUpload / b.mail / b.objectStore) call gate.check(ctx) at their byte-boundary moment with a uniform context. The decision shape is { ok, action, sanitized?, issues, contentTypeOverride?, headers?, forensicHash, forensicSnapshot?, runtimeMs, cacheKey? }.

Operator extension surface inherited by every member:

Module-level constants ACTIONS / MODES / ISSUE_SEVERITIES carry the frozen enums every guard validates against.

Foundation for the guard-* family. Every content-safety primitive shipped under b.guard* composes through buildGuardGate, makeProfileBuilder, makeRulePackLoader, and lookupCompliancePosture; b.guardAll aggregates the registered guards into a single security-on-by-default gate.

b.gateContract.GateContractError #

stable0.7.5

FrameworkError subclass thrown by gate-contract entry points on shape violations: gate-contract/bad-shape from validateGateShape, gate-contract/bad-opt from defineGate / cachingGate / workerThreadGate, gate-contract/profile-cycle and gate-contract/unknown-profile from buildProfile. alwaysPermanent — never retried by b.retry.

try {
  b.gateContract.validateGateShape({}, "broken");
} catch (e) {
  e instanceof b.gateContract.GateContractError;     // → true
  e.code;                                            // → "gate-contract/bad-shape"
}

b.gateContract.ACTIONS #

stable0.7.5

Frozen list of every action a gate decision is allowed to set. serve emits the bytes unchanged; refuse rejects with an operator-meaningful error; sanitize substitutes decision.sanitized for the original bytes; strip removes the offending content; audit-only serves but emits an audit entry; warn serves and emits a warning counter; challenge-mfa triggers step-up auth before serving; deny-and-revoke rejects and invalidates the actor's session.

b.gateContract.ACTIONS.indexOf("serve");             // → 0
b.gateContract.ACTIONS.indexOf("warp-speed");        // → -1
Object.isFrozen(b.gateContract.ACTIONS);             // → true

b.gateContract.MODES #

stable0.7.5

Frozen list of mode-posture values a gate can run in. enforce honors the decision; warn-only translates every refuse to warn for staged rollout; shadow runs alongside a primary and never refuses (observability-only); audit-only and log-only emit an audit entry but never block; canary enforces on a sampled subset and warns on the rest.

b.gateContract.MODES.indexOf("enforce");             // → 0
b.gateContract.MODES.indexOf("yolo");                // → -1

b.gateContract.ISSUE_SEVERITIES #

stable0.7.5

Frozen list of severity levels a guard issue may carry. info and warn are observability-only — aggregateIssues keeps ok: true with them present. high and critical flip the result to ok: false, refusing the input.

b.gateContract.ISSUE_SEVERITIES;                     // → ["info","warn","high","critical"]
b.gateContract.ISSUE_SEVERITIES.indexOf("critical"); // → 3

b.gateContract.validateGateShape(gate, label, errorClass) #

stable0.7.5

Throws when gate does not satisfy the contract — gate.check must be a function, gate.mode (when present) must be one of the MODES enum values, and gate.metrics / gate.close (when present) must be functions. Operator-supplied gates (and framework-supplied gates with operator-toggled hooks) all flow through this check at host-primitive wire-up time. Shape errors at boot are cheaper than at request time. Returns gate unchanged on success.

var gate = b.guardCsv.gate({ profile: "strict" });
b.gateContract.validateGateShape(gate, "uploads.csv");
// → returns the gate unchanged when shape is valid

try {
  b.gateContract.validateGateShape({}, "broken");
} catch (e) {
  e.code;                                            // → "gate-contract/bad-shape"
}

b.gateContract.defineGate(opts) #

stable0.7.5
{
  name:                  string,           // identifier surfaced in audit / counters
  version:               string,           // semver default "1.0.0"
  mode:                  string,           // one of MODES; default "enforce"
  check:                 function,         // async (ctx) → decision
  beforeCheck:           function|null,    // (ctx) → { skip?, transform? }
  afterCheck:            function|null,    // (ctx, decision) → decision
  onIssue:               function|null,    // (issue, ctx) → issue|{suppress|promote}
  onSanitize:            function|null,    // (bytes, sanitized, ctx) → sanitized
  onRefuse:              function|null,    // (ctx, decision) → void
  onAudit:               function|null,    // (entry) → entry|false (false suppresses)
  audit:                 object|null,      // b.audit handle
  observability:         object|null,      // b.observability handle
  forensicEvidenceStore: object|null,      // { write({ ... }) }
  forensicSnippetBytes:  number,           // 0 = disabled
  cache:                 object|null,      // b.cache shape
  cacheTtlMs:            number,
  maxRuntimeMs:          number,           // 0 = uncapped
  ruleHash:              string,           // override fingerprint
}

Build a gate that satisfies the contract. Wraps the operator-supplied check(ctx) with the cross-cutting concerns (hooks, observability, forensic snapshot, runtime cap, decision cache, mode-posture translation) so guards only write the per-guard inspection logic. Returns a gate exposing { check, mode, audit, observability, metrics, reset, close, name, version, ruleHash, dryRun, policyDiff }. Most guards forward through b.gateContract.buildGuardGate instead — defineGate is the lower-level factory used by host-side composers (composeGates / byRoute / etc.).

var gate = b.gateContract.defineGate({
  name: "tenant:csv:strict",
  mode: "enforce",
  maxRuntimeMs: 250,
  check: async function (ctx) {
    var text = b.gateContract.extractBytesAsText(ctx);
    if (text.indexOf("=cmd|") === 0) {
      return { ok: false, action: "refuse",
               issues: [{ kind: "csv.formula-injection", severity: "high" }] };
    }
    return { ok: true, action: "serve" };
  },
});
var d = await gate.check({ bytes: Buffer.from("name,age\nada,36") });
d.action;                                            // → "serve"

b.gateContract.runGate(gate, ctx, opts?) #

stable0.7.5
{
  // reserved for future host-side cross-cutting concerns; pass `{}` today
}

Execute a gate's check(ctx) and return its decision. When gate is null or has no check function, returns the canonical { ok: true, action: "serve" } shape — host primitives invoke runGate with an operator-configurable gate that may legitimately be unset. The opts argument is reserved for future host-side cross-cutting concerns and is currently unused.

var gate = b.guardCsv.gate({ profile: "strict" });
var decision = await b.gateContract.runGate(gate, {
  bytes:    Buffer.from("name,age\nada,36"),
  route:    "/api/imports",
  filename: "people.csv",
});
decision.action;                                     // → "serve"

// Unset gate is a no-op serve.
(await b.gateContract.runGate(null, {})).action;     // → "serve"

b.gateContract.composeGates(gates, opts?) #

stable0.7.5
{
  name:             string,    // wrapper gate name (default "composed")
  firstRefusalWins: boolean,   // default true
}

Chain a list of gates left-to-right. First refusal wins. When a gate returns action: "sanitize" and firstRefusalWins is true (default), the sanitized bytes feed into the next gate's context — letting an HTML sanitizer hand its scrubbed output to a downstream link-shape guard. Returns a wrapping gate that satisfies the contract (so the composition is itself composable).

var bidi  = b.guardCsv.gate({ profile: "strict" });
var pii   = b.guardCsv.gate({ compliancePosture: "hipaa" });
var chain = b.gateContract.composeGates([bidi, pii], { name: "csv:chain" });
var d = await chain.check({ bytes: Buffer.from("name,ssn\nada,123-45-6789") });
d.action;                                            // → "refuse"

b.gateContract.multiplexGates(gateMap, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "multiplex")
}

File-extension-keyed gate dispatch. Looks at ctx.filename, extracts the lowercased final extension (.csv / .html / etc.), and dispatches to the matching gate. The "default" key serves as the fallback; missing entries (no key match, no fallback) return the canonical serve decision so host primitives can wire a single mux gate without per-extension special-casing.

var mux = b.gateContract.multiplexGates({
  ".csv":  b.guardCsv.gate({ profile: "strict" }),
  ".html": b.guardHtml.gate({ profile: "strict" }),
  "default": b.guardCsv.gate({ profile: "permissive" }),
});
var d = await mux.check({ bytes: Buffer.from("a,b\n1,2"), filename: "x.csv" });
d.action;                                            // → "serve"

b.gateContract.contentTypeMux(gateMap, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "contentTypeMux")
}

Content-Type-keyed gate dispatch. Reads ctx.contentType, strips parameters (; charset=utf-8), lowercases, and routes to the matching gate. The "default" key is the fallback; unknown types (no key match, no fallback) serve uninspected. Useful when one route accepts multiple media types and each needs its own guard.

var mux = b.gateContract.contentTypeMux({
  "text/csv":   b.guardCsv.gate({ profile: "strict" }),
  "text/html":  b.guardHtml.gate({ profile: "strict" }),
  "default":    b.guardCsv.gate({ profile: "permissive" }),
});
var d = await mux.check({
  bytes:       Buffer.from("name,age\nada,36"),
  contentType: "text/csv; charset=utf-8",
});
d.action;                                            // → "serve"

b.gateContract.byActorTier(gateMap, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "byActorTier")
}

Actor-tier-keyed gate dispatch. Reads ctx.actor.tier (e.g. "free" / "paid" / "admin") and routes to the matching gate. Falls back to gateMap["default"] when the tier is missing or unmapped; missing fallback serves uninspected. Lets free-tier tenants run a stricter posture than paid customers without branching at every call site.

var byTier = b.gateContract.byActorTier({
  free:    b.guardCsv.gate({ profile: "strict" }),
  paid:    b.guardCsv.gate({ profile: "balanced" }),
  default: b.guardCsv.gate({ profile: "strict" }),
});
var d = await byTier.check({
  bytes: Buffer.from("name,age\nada,36"),
  actor: { tier: "paid" },
});
d.action;                                            // → "serve"

b.gateContract.byRoute(gateMap, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "byRoute")
}

Route-pattern-keyed gate dispatch. Patterns are simple glob-prefix matches — /admin/* matches every path beginning with /admin/. Tries each entry in declaration order, falling back to "*" / "default"; missing fallback serves uninspected. Lets /admin/* routes apply a stricter guard than the public surface without threading per-route opts through every call site.

var byPath = b.gateContract.byRoute({
  "/admin/*": b.guardCsv.gate({ profile: "strict" }),
  "/api/*":   b.guardCsv.gate({ profile: "balanced" }),
  "*":        b.guardCsv.gate({ profile: "permissive" }),
});
var d = await byPath.check({
  bytes: Buffer.from("name,age\nada,36"),
  route: "/admin/imports",
});
d.action;                                            // → "serve"

b.gateContract.byDirection(gateMap, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "byDirection")
}

Direction-aware gate dispatch. Reads ctx.direction ("inbound" or "outbound"; default "outbound") and routes to the matching gate. Lets a single guard wiring run a stricter posture on bytes arriving from an external source than on bytes the framework is about to emit. Missing direction maps serve uninspected.

var byDir = b.gateContract.byDirection({
  inbound:  b.guardCsv.gate({ profile: "strict" }),
  outbound: b.guardCsv.gate({ profile: "balanced" }),
});
var d = await byDir.check({
  bytes:     Buffer.from("name,age\nada,36"),
  direction: "inbound",
});
d.action;                                            // → "serve"

b.gateContract.shadowMode(primary, candidate, opts?) #

stable0.7.5
{
  name: string,   // wrapper gate name (default "shadow")
}

Run a candidate gate alongside the primary; emit a divergence counter when their actions disagree. The primary's decision is the one honored — candidate runs are observability-only and don't block the request. Useful for staged rollout of a new profile (run it shadowed for a week, watch the divergence rate, then promote it to primary).

var primary   = b.guardCsv.gate({ profile: "strict" });
var candidate = b.guardCsv.gate({ profile: "balanced" });
var staged    = b.gateContract.shadowMode(primary, candidate, { name: "csv:staged" });
var d = await staged.check({ bytes: Buffer.from("name,age\nada,36") });
d.action;                                            // → "serve"  (primary's decision)

b.gateContract.canaryGate(gate, opts?) #

stable0.7.5
{
  rate: number,   // 0..1, default 0.1
  name: string,   // wrapper gate name (default "canary")
}

Enforce the wrapped gate's refuse decisions on rate of requests; downgrade the rest to warn. Default rate is 0.1 (10% enforced, 90% warned). Sampling uses a non-cryptographic random source — fine for rollout shaping, never for security-critical sampling.

var strict = b.guardCsv.gate({ profile: "strict" });
var canary = b.gateContract.canaryGate(strict, { rate: 0.25 });
var d = await canary.check({ bytes: Buffer.from("name,age\nada,36") });
d.ok;                                                // → true

b.gateContract.cachingGate(gate, opts) #

stable0.7.5
{
  backend: object,   // b.cache-shaped { get, set } (required)
  ttlMs:   number,   // cache TTL
  name:    string,   // wrapper gate name (default ":cached")
}

Wrap a gate with an explicit shared cache backend. The per-gate built-in cache (configured via defineGate({ cache, cacheTtlMs })) is per-gate-instance; this wrapper is the operator-side variant for sharing one cache across multiple gates. opts.backend must expose the b.cache shape ({ get(key), set(key, value, opts) }).

var cache  = b.cache.create({ backend: "memory", maxEntries: 10000 });
var strict = b.guardCsv.gate({ profile: "strict" });
var cached = b.gateContract.cachingGate(strict, {
  backend: cache,
  ttlMs:   60000,
});
var d = await cached.check({ bytes: Buffer.from("name,age\nada,36") });
d.action;                                            // → "serve"

b.gateContract.workerThreadGate(gate, opts) #

stable0.7.5
{
  worker: object,   // b.worker-shaped { run } (required)
  name:   string,   // wrapper gate name (default ":worker")
}

Offload a gate's check(ctx) to a worker. opts.worker must expose run({ gate, ctx }) returning the decision (matches the b.worker shape). Useful when a guard's per-request CPU cost (large-doc HTML parsing, archive entry inspection) is high enough that running it on the request thread would impact throughput.

var worker = b.worker.create({ pool: 4, modulePath: "./guards/csv-worker.js" });
var strict = b.guardCsv.gate({ profile: "strict" });
var offloaded = b.gateContract.workerThreadGate(strict, { worker: worker });
var d = await offloaded.check({ bytes: Buffer.from("name,age\nada,36") });
d.action;                                            // → "serve"

b.gateContract.makeProfileBuilder(profiles) #

stable0.7.5

Closes over a guard's PROFILES map and returns a buildProfile(opts) function that delegates to the recursive composition entry point. Every guard's buildProfile export is therefore a single binding, not a duplicate forwarding wrapper. The returned function accepts { baseProfile, extends, overrides, removes } plus inline keys, and resolves names through the closed-over profile table.

var PROFILES = {
  strict:    { formulaInjectionPolicy: "reject",     bidiCharPolicy: "reject" },
  balanced:  { formulaInjectionPolicy: "prefix-tab", bidiCharPolicy: "strip"  },
};
var buildProfile = b.gateContract.makeProfileBuilder(PROFILES);
var custom = buildProfile({
  baseProfile: "strict",
  overrides:   { trailingWhitespacePolicy: "preserve" },
});
custom.formulaInjectionPolicy;                       // → "reject"
custom.trailingWhitespacePolicy;                     // → "preserve"

b.gateContract.lookupCompliancePosture(name, postures, errorFactory, codePrefix) #

stable0.7.5hipaapci-dssgdprsoc2

Look up a compliance-posture overlay by name. Throws errorFactory(codePrefix + ".bad-posture") when the name is not in the posture map; returns a shallow clone of the posture object otherwise. Every guard's compliancePosture(name) export forwards here so the error code, error class, and clone semantics stay identical across the family.

var POSTURES = {
  hipaa:   { piiPolicy: "redact", bidiCharPolicy: "reject" },
  "pci-dss": { piiPolicy: "redact", bidiCharPolicy: "reject" },
};
var posture = b.gateContract.lookupCompliancePosture(
  "hipaa", POSTURES, b.guardCsv.GuardCsvError.factory, "csv");
posture.piiPolicy;                                   // → "redact"

b.gateContract.makePostureAccessor(postures, opts?) #

stable0.15.14
{
  fallback: any,   // value returned for an unknown / proto-key name (default null)
}

Build the public compliancePosture(name) accessor a guard exposes — maps a compliance-posture name through the guard's own postures table to the profile it selects, returning opts.fallback (default null) for an unknown name. Folds the one-line lookup the mail-scanner / content-detect factories each redefined verbatim (return POSTURES[name] || null) into one proto-shadow-safe helper: the membership test is hasOwnProperty.call so a prototype key (constructor / __proto__ / toString) resolves to the fallback rather than an inherited Function. Unlike lookupCompliancePosture it does NOT throw and returns the raw mapped value (a profile name string), not an object copy.

var compliancePosture = b.gateContract.makePostureAccessor(COMPLIANCE_POSTURES);
compliancePosture("hipaa");        // → "strict"
compliancePosture("constructor");  // → null  (proto key, not an own posture)

b.gateContract.makeProfileResolver(cfg) #

stable0.15.0
{
  profiles:   object,    // the guard's PROFILES map; required
  postures:   object,    // COMPLIANCE_POSTURES (posture -> profile name)
  defaults:   string,    // fallback profile name when no posture/profile given
  errorClass: function,  // the guard's FrameworkError subclass
  codePrefix: string,    // error-code namespace (e.g. "mail-compose")
  byObject:   boolean,   // true -> return the profile config object, not its name
}

Closes over a guard's profile config and returns a resolveProfile(opts) function: maps opts.posture through the compliance-posture table, else falls back to opts.profile || cfg.defaults, validates the name against cfg.profiles, and throws cfg.errorClass.factory(cfg.codePrefix + "/bad-profile") on an unknown name. The sibling of makeProfileBuilder / makeRulePackLoader / lookupCompliancePosture for the resolution step — every defineParser-shaped line-protocol / mail / agent guard reuses it instead of re-declaring an identical _resolveProfile.

var resolveProfile = b.gateContract.makeProfileResolver({
  profiles: PROFILES, postures: COMPLIANCE_POSTURES,
  defaults: "strict", errorClass: GuardMailComposeError,
  codePrefix: "mail-compose",
});
resolveProfile({ posture: "hipaa" });   // → "strict"

b.gateContract.resolveProfileName(opts, postures, defaultProfile) #

stable0.15.13
{
  profile:   string,   // explicit profile name — wins when present
  posture:   string,   // compliance posture, mapped through `postures`
}

Resolve a profile NAME from create-time opts with PROFILE precedence: an explicit opts.profile wins, else opts.posture mapped through the compliance-posture table, else defaultProfile. Returns the name WITHOUT validating it — the caller checks membership in its own PROFILES map and throws its own typed, field-specific error. This is the resolution EXPRESSION the mail-scanner / envelope factories (mail-greylist / mail-rbl / mail-scan / mail-spam-score / mail-helo / guard-envelope) each hand-rolled identically.

It differs from makeProfileResolver in two deliberate ways: it does not throw (so the caller keeps its bespoke bad-profile message) and it gives profile precedence rather than posture precedence. The two precedences coexist in the framework today (the defineParser-shaped guards resolve posture-first); unifying them is a policy decision, and routing every caller through one of these two helpers is what makes that decision a single edit.

var name = b.gateContract.resolveProfileName(
  { profile: "balanced" }, COMPLIANCE_POSTURES, "strict");
// → "balanced"

b.gateContract.throwOnRefusalSeverity(issues, cfg) #

stable0.15.0
{
  errorClass: function,  // the guard's FrameworkError subclass; required
  codePrefix: string,    // error-code namespace; the `.refused` fallback code
  op:         string,    // operation name in the message (default "sanitize")
  severities: string[],  // refusal severities (default ["critical","high"])
}

Throw on the first critical/high-severity issue in a detector's issue list — the refusal step every guard sanitize runs after detection (sanitize can serve a clean value but never repair a critical/high finding). Builds the guard's error via cfg.errorClass.factory with code issue.ruleId || (cfg.codePrefix + ".refused") and message guard.: (op default "sanitize"; the guard identity derives from the error class name). The throw sibling of aggregateIssues (which returns { ok, issues } instead of throwing) — replaces the per-guard hand-rolled severity-gating loop.

var issues = detect(input, opts);
b.gateContract.throwOnRefusalSeverity(issues, {
  errorClass: GuardCidrError, codePrefix: "cidr",
});
// throws GuardCidrError(ruleId || "cidr.refused", "guardCidr.sanitize: " + snippet)
// on the first critical/high issue

b.gateContract.ALL_STRICT_POSTURES #

stable0.15.0hipaapci-dssgdprsoc2

Canonical strict-all COMPLIANCE_POSTURES map every command/parser guard composes. Maps each of the four baseline regulatory postures — hipaa / pci-dss / gdpr / soc2 — onto the guard's strict profile name. Guards whose four postures all resolve to strict (the command/protocol validators: POP3 / IMAP / SMTP / ManageSieve commands, mail-compose / query / sieve / move / reply, the envelope and event-bus shapes, the mail pipeline scorers, and the safe-* line-protocol parsers) reference this single frozen object instead of re-declaring it. Guards that overlay per-posture byte-limits or redaction flags (the content guards: CSV / HTML / JSON / XML / YAML / JWT / OAuth / template, etc.) keep their own posture map and do not compose this.

Frozen once and shared by reference: every consumer reads it through its own COMPLIANCE_POSTURES binding and never mutates it.

var COMPLIANCE_POSTURES = b.gateContract.ALL_STRICT_POSTURES;
COMPLIANCE_POSTURES.hipaa;                            // → "strict"
Object.isFrozen(COMPLIANCE_POSTURES);                 // → true

b.gateContract.CHAR_THREATS_REJECT_ALL #

stable0.15.13hipaapci-dssgdprsoc2

The universal character-safety floor: the four invisible-character threats — BIDI overrides, C0/C1 control bytes, embedded null bytes, and zero-width characters — each set to "reject". These four classes are categorically unsafe in an identifier or structured value (forgery, log injection, label-segmentation, parser confusion), so every identifier/protocol guard refuses them in every profile tier and the content guards refuse them in strict.

Spread this frozen block into a profile tier instead of re-declaring the four lines: { ...gateContract.CHAR_THREATS_REJECT_ALL, ... }. A tier that relaxes one class overrides after the spread (e.g. { ...CHAR_THREATS_REJECT_ALL, zeroWidthPolicy: "strip" }), keeping the floor for the other three. Frozen and shared by reference; the spread copies the values into each consumer's own tier object.

var PROFILES = Object.freeze({
  strict: { ...b.gateContract.CHAR_THREATS_REJECT_ALL, maxBytes: 256 },
});
PROFILES.strict.bidiPolicy;                           // → "reject"
Object.isFrozen(b.gateContract.CHAR_THREATS_REJECT_ALL); // → true

b.gateContract.DANGEROUS_URL_SCHEMES #

stable0.15.13soc2

The frozen denylist of URL schemes that are categorically unsafe inside a markup attribute value (href / src / xlink:href) — the markup XSS and dangerous-resource vector set. javascript / vbscript / livescript / mocha / ecmascript execute script; data / view-source / mhtml / feed carry or expose renderable content; file / jar / intent reach local resources or protocol handlers. A markup sanitizer rejects an attribute whose scheme is in this list.

Lower-cased, scheme-name only (no trailing colon) so callers compare against a lower-cased parsed scheme via indexOf(scheme) !== -1. This is the markup-attribute DENYLIST — distinct from b.safeUrl's protocol ALLOWLIST, which governs full-URL parsing where only an explicit set of protocols is permitted.

b.gateContract.DANGEROUS_URL_SCHEMES.indexOf("javascript");  // → 0 (dangerous)
b.gateContract.DANGEROUS_URL_SCHEMES.indexOf("https");       // → -1 (allowed)
Object.isFrozen(b.gateContract.DANGEROUS_URL_SCHEMES);       // → true

b.gateContract.SAFE_URL_SCHEMES #

stable0.15.13soc2

The frozen base allowlist of URL schemes a markup sanitizer accepts in an attribute value at the strict tier — http / https / mailto / tel. A guard extends it for looser tiers (e.g. SAFE_URL_SCHEMES.concat(["ftp"])) rather than re-declaring the base. Scheme names only, no trailing colon.

b.gateContract.SAFE_URL_SCHEMES;                       // → ["http","https","mailto","tel"]
Object.isFrozen(b.gateContract.SAFE_URL_SCHEMES);      // → true

b.gateContract.identifierFixtures(benign, hostile, encoding?) #

stable0.15.13

Build an identifier guard's frozen INTEGRATION_FIXTURES from one benign and one hostile sample string. The layer-5 host harness feeds the string form to gate.check({ identifier }) and the byte form to the upload / digest paths, so the two are the same value in two representations. A guard that hand-writes both forms repeats its sample literal twice (benignBytes: Buffer.from("x"), benignIdentifier: "x"); declaring the string once and deriving the buffer removes that per-guard duplication. encoding defaults to "utf8" — pass "ascii" for line-protocol command samples whose bytes must stay single-octet.

var INTEGRATION_FIXTURES =
  b.gateContract.identifierFixtures("example.com", "192.168.1.1");
INTEGRATION_FIXTURES.benignIdentifier;      // → "example.com"
INTEGRATION_FIXTURES.benignBytes;           // → Buffer "example.com"
Object.isFrozen(INTEGRATION_FIXTURES);      // → true

b.gateContract.compliancePostures(profiles, spec) #

stable0.15.13hipaapci-dssgdprsoc2
{
  {
    base:     number,    // required, positive even byte count: the hipaa/pci snippet budget
    overlays: {          // optional per-posture policy deltas, merged last
      hipaa:     object,
      "pci-dss": object,
      gdpr:      object,
      soc2:      object,
    },
  }
}

Build a content guard's four-posture COMPLIANCE_POSTURES map from its profile set and a single forensic-snippet budget, encoding the framework's regulation-disposition policy in one place instead of re-declaring it in every guard. Each regulation maps to the profile tier whose disposition matches its intent:

- hipaa / pci-dss / soc2 → the strict profile. These regimes demand forensic integrity — the record must not be silently altered — so every threat class is rejected, never sanitized. - gdpr → the balanced profile. Data-minimization favors removing the offending bytes over rejecting the whole value, so on free-text content the balanced tier strips the sanitizable character classes (bidi / control / zero-width) while still rejecting structural threats. For an identifier guard the balanced tier rejects those classes too — stripping bytes from an identifier would change its identity — so the disposition follows the guard's own content kind automatically.

The forensic snippet budget scales with each regime's retention posture: hipaa / pci-dss keep base bytes, gdpr keeps base / 2 (retain less hostile data under data-minimization), soc2 keeps base * 2 (audit retention). Pass spec.overlays to layer a deliberate per-posture delta on top of the tier — e.g. a filename guard stripping bidi / control under gdpr where its balanced profile would reject them. Each returned posture is frozen and shared by reference.

var COMPLIANCE_POSTURES = b.gateContract.compliancePostures(PROFILES, {
  base: 256,
});
COMPLIANCE_POSTURES.gdpr.forensicSnippetBytes;       // → 128
Object.isFrozen(COMPLIANCE_POSTURES.hipaa);          // → true

b.gateContract.strictDefaults(profiles, overlay?) #

stable0.15.13
{
  overlay: object   // optional per-guard default overrides merged last (e.g. { maxRuntimeMs: C.TIME.seconds(10) }); may override `mode`
}

Build a guard's frozen DEFAULTS opts: its strict profile, in enforce mode, plus any per-guard overlay. Every guard's no-opts call path starts from the strictest profile with enforcement on (security-on by default); the only variation is a guard that adds a parse runtime cap (maxRuntimeMs) or another default override. Replaces the hand-rolled Object.freeze(Object.assign({}, PROFILES["strict"], { mode: "enforce", … })) every guard repeated.

var DEFAULTS = b.gateContract.strictDefaults(PROFILES);                          // strict + enforce
var DEFAULTS = b.gateContract.strictDefaults(PROFILES, { maxRuntimeMs: 10000 }); // + a parse runtime cap

b.gateContract.makeRulePackLoader(errorClass, codePrefix) #

stable0.7.5

Build a per-guard rule-pack registry. Returns { load(pack), list(), get(id) }. load validates that pack is an object with a non-empty string pack.id (throwing errorClass(codePrefix + ".bad-opt") when not) and stores it in a closed-over map keyed by pack.id. list returns the stored packs; get(id) returns one or null. Used so every guard's loadRulePack export shares storage shape and validation.

var packs = b.gateContract.makeRulePackLoader(b.guardCsv.GuardCsvError, "csv");
packs.load({
  id: "pii-extra",
  rules: [{ id: "ssn", severity: "critical",
            detect: function (cell) { return /^\d{3}-\d{2}-\d{4}$/.test(cell); } }],
});
packs.get("pii-extra").rules.length;                 // → 1

b.gateContract.extractBytesAsText(ctx) #

stable0.7.5

Read ctx.bytes and return a UTF-8 string for inspection. Centralizes the string-or-Buffer-or-empty handling so each guard's check(ctx) body deals with the inspection logic only. Returns "" when ctx.bytes is missing — callers treat empty as the serve case.

var ctx  = { bytes: Buffer.from("name,age\nada,36") };
var text = b.gateContract.extractBytesAsText(ctx);
text;                                                // → "name,age\nada,36"

b.gateContract.extractBytesAsText({});               // → ""
b.gateContract.extractBytesAsText({ bytes: "x,y" }); // → "x,y"

b.gateContract.buildGuardGate(name, opts, check) #

stable0.7.5
{
  mode:                  string,        // one of MODES; default "enforce"
  audit:                 object|null,   // b.audit handle for emission
  observability:         object|null,   // b.observability handle
  forensicEvidenceStore: object|null,   // { write({ ... }) }
  forensicSnippetBytes:  number,        // 0 = disabled
  cache:                 object|null,   // b.cache shape
  cacheTtlMs:            number,
  maxRuntimeMs:          number,        // 0 = uncapped
  beforeCheck:           function|null,
  afterCheck:            function|null,
  onIssue:               function|null,
  onSanitize:            function|null,
  onRefuse:              function|null,
  onAudit:               function|null,
}

Gate-construction shorthand for guard-* primitives. Forwards the uniform ~16-key opts bag (mode, audit, observability, forensicEvidenceStore, forensicSnippetBytes, cache, cacheTtlMs, maxRuntimeMs, all six lifecycle hooks) to defineGate, so each guard's gate(opts) body is just the per-guard check function plus a label. Result satisfies validateGateShape.

var myGuardGate = b.gateContract.buildGuardGate(
  "myGuard:strict",
  { mode: "enforce", maxRuntimeMs: 250 },
  async function (ctx) {
    var text = b.gateContract.extractBytesAsText(ctx);
    if (text.length === 0) return { ok: true, action: "serve" };
    if (/\s/.test(text)) {
      return { ok: false, action: "refuse",
               issues: [{ kind: "whitespace", severity: "high" }] };
    }
    return { ok: true, action: "serve" };
  });
var d = await myGuardGate.check({ bytes: Buffer.from("hello") });
d.action;                                            // → "serve"

b.gateContract.severityDisposition(issues) #

stable0.15.13

The non-sanitizing guard gate's severity action-chain in one place. A guard that cannot repair its subject (an auth bundle, an OAuth flow, a GraphQL request, image / PDF metadata, an archive entry list, an email body, a regex pattern) ends its gate check with the identical disposition: serve when there are no findings, audit-only when no finding reaches refusal severity, else refuse. This is the sibling of buildContentGate (which adds the sanitize attempt for content that CAN be repaired). Each guard keeps its own subject extraction + validate call, then returns severityDisposition(rv.issues).

A finding of critical OR high severity refuses; anything lower is audit-only. The returned shape matches a gate check result: { ok, action } (no issues on a clean serve) or { ok, action, issues }.

var rv = module.exports.validate(bundle, opts);
return b.gateContract.severityDisposition(rv.issues);
// [] → { ok: true, action: "serve" }
// [{ severity: "low" }] → { ok: true, action: "audit-only", issues: [...] }
// [{ severity: "high" }] → { ok: false, action: "refuse", issues: [...] }

b.gateContract.buildContentGate(spec) #

stable0.15.13
{
  name:                  string,                   // gate label (audit/metric/cache identity)
  opts:                  object,                   // already resolved profile/posture opts
  validate:              function,                 // (subject, opts) -> { ok, issues }
  produceSanitized:      function,                 // (subject, opts) -> Buffer|string
  ctxField:              "text"|"bytes",           // default "text" (extractBytesAsText); "bytes" reads ctx.bytes raw
  sanitizeBlockingKinds: string[],                 // issue kinds that skip the sanitize attempt (e.g. ["svgz-compressed"])
}

The content-guard gate action-chain in one place. Every content guard's gate(opts) ran the identical chain — extract the bytes, serve a clean input, audit-only when no issue reaches the refusal severity, attempt sanitize when the input is eligible, else refuse — differing only in declarative axes. Passing those axes to one primitive replaces ~30 lines of per-guard gate body and makes the chain impossible to drift between guards.

A CRITICAL finding always refuses — too severe to serve even sanitized (a stripped script can still carry an mXSS / parser-differential vector). Only HIGH findings are sanitize-eligible, and even then the action is PROVEN, not guessed: the gate runs produceSanitized then RE-VALIDATES its output, returning sanitize only when the result is verifiably clean — otherwise refuse. An operator's reject choice lands as critical (→ refuse) and a strip choice as high (→ sanitize-if-verified), so the severity carries the disposition with no per-policy bookkeeping — replacing a global "is any policy set to reject?" guess that wrongly froze sanitize for findings unrelated to the rejected policy. The sanitizer's own policy-respecting behaviour does the rest: it throws on / leaves a reject-class finding so the re-validate still refuses, and refuses anything it cannot actually repair. sanitizeBlockingKinds skips the attempt for inputs a text sanitizer must not touch (gzipped SVGZ bytes); a thrown producer falls through to refuse.

var g = b.gateContract.buildContentGate({
  name: "guardXml:strict", opts: resolved, validate: validate,
  produceSanitized: function (t, o) { return sanitize(t, o); },
});
(await g.check({ bytes: Buffer.from("1") })).action;   // → "serve"

b.gateContract.policyDisposition(policy) #

stable0.15.13

Map an operator content-policy value to the gate disposition it selects. A content guard emits a finding only when the governing policy is not allow; this turns that policy into what the gate should DO with the finding — independent of the finding's impact severity:

- rejectrefuse (operator chose to reject this class outright) - audit / audit-onlyaudit (observe, do not block or alter) - a known mitigation (strip, prefix-tab, prefix-quote, wrap-with-quotes-and-prefix, allowlist, redact, trim) → sanitize (the guard's sanitizer performs the chosen transform)

Fails CLOSED: an unrecognized policy value (a typo such as rejet, or a mitigation name not in the known set) maps to refuse, never sanitize — a misconfiguration must not silently downgrade a finding to serve-after- best-effort. Add a new mitigation to MITIGATION_POLICIES when one ships.

b.gateContract.policyDisposition("reject");      // → "refuse"
b.gateContract.policyDisposition("strip");       // → "sanitize"
b.gateContract.policyDisposition("audit-only");  // → "audit"
b.gateContract.policyDisposition("rejet");       // → "refuse" (fail closed)

b.gateContract.charThreatDisposition(issue, opts) #

stable0.15.13
{
  bidiPolicy:      string,   // governs the bidi-override finding
  nullBytePolicy:  string,   // governs the null-byte finding
  controlPolicy:   string,   // governs the control-char finding
  zeroWidthPolicy: string,   // governs the zero-width finding
}

Gate disposition for the shared character-threat findings every content guard collects via codepointClass.detectCharThreatsbidi-override, null-byte, control-char — resolved from the guard's per-class policy (bidiPolicy / nullBytePolicy / controlPolicy). Returns null for any other kind so a guard's own dispositionFor can fall through to it for the shared kinds and handle its guard-specific findings itself.

function dispositionFor(issue, opts) {
  return b.gateContract.charThreatDisposition(issue, opts) ||
         mySpecificMapping(issue, opts);
}

b.gateContract.aggregateIssues(issues) #

stable0.7.5

Wrap an issues array in the canonical { ok, issues } validate-result shape. ok is true only when no issue carries critical or high severity — info and warn issues do not flip ok. Used by guards whose validate path can't route through runIssueValidator (raw-Buffer input cases such as svg magic detection or filename byte scans).

var result = b.gateContract.aggregateIssues([
  { kind: "csv.bidi", severity: "high",
    snippet: "U+202E embedded in cell" },
  { kind: "csv.trailing-whitespace", severity: "info" },
]);
result.ok;                                           // → false
result.issues.length;                                // → 2

b.gateContract.badInputResultIfNotStringOrBuffer(input) #

stable0.7.5

Type-guard for guard-* validate entry points. Returns the canonical { ok: false, issues: [{ kind: "bad-input", severity: "high", ... }] } result when input is neither a string nor a Buffer; null otherwise. Used by guards whose validate path can't pre-convert — b.guardSvg needs raw bytes for SVGZ magic detection, b.guardFilename needs raw bytes for the overlong-UTF-8 byte scan.

b.gateContract.badInputResultIfNotStringOrBuffer("hello");      // → null
b.gateContract.badInputResultIfNotStringOrBuffer(Buffer.from("x")); // → null
var bad = b.gateContract.badInputResultIfNotStringOrBuffer(42);
bad.ok;                                              // → false
bad.issues[0].kind;                                  // → "bad-input"

b.gateContract.detectStringInput(input, opts, cfg) #

stable0.15.13
{
  {
    name:      string,    // required: the guard name — ruleId prefix + default noun
    noun:      string,    // default name — the subject word in the bad-input/empty snippet
    emptyMode: string,    // "issue" (default) → .empty issue · "ok" → [] (empty is legal) · "skip" → no empty check
    cap: {                // omit when the guard has no byte cap
      bytes:   number,            // required: the byte limit
      kind:    string,            // default "-cap" — the cap issue kind (and ruleId suffix)
      snippet: string|function,   // default " input exceeds maxBytes "; fn(byteLen, bytes) when it needs the measured length
    },
    scanCodepoints: boolean,      // default true: the not-done result carries the codepoint-class scan. Pass false for a guard that scans codepoints later in its own detection (or parses them via its format, e.g. JSON) — the not-done result is then `[]`.
  }
}

The whole detector preamble every raw-contract string guard opens with: reject a non-string input, then an empty one, then one over the byte cap, else collect the codepoint-class threats (BIDI / control / null / zero-width) the guard then appends its own findings to. A guard on the raw input contract owns its own input check (see INPUT_CONTRACTS); this builds that preamble once, guard-named, instead of re-spelling its four steps in every _detectIssues. Returns { done, issues }: when done the detector returns issues verbatim (the .bad-input / .empty / cap issue, or [] for a legal empty); when not done, issues is the codepoint-threat list the detector continues from. The byte cap runs before the codepoint scan so a huge input is rejected before the O(n) scan.

The cap's divergence is data, not branching: cap.bytes is the limit (the guard's resolved maxBytes / maxPatternBytes / maxDomainOctets), cap.kind the issue kind (default -cap), and cap.snippet the message — a string, or a function(byteLen, bytes) when it embeds the measured length. Omit cap for a guard with no byte cap.

function _detectIssues(input, opts) {
  var pre = b.gateContract.detectStringInput(input, opts, {
    name: "cidr", cap: { bytes: opts.maxBytes },
  });
  if (pre.done) return pre.issues;
  var issues = pre.issues;            // codepoint-class threats so far
  // … guard-specific detection appends to issues …
  return issues;
}

b.gateContract.runIssueValidator(input, opts, detector, contract?) #

stable0.7.5
{
  ...:   any,                     // detector-defined; passed through to detector(subject, opts)
}

The single validate(input, opts) engine for the whole guard family. An input contract normalizes the raw input to the subject the detector expects (or flags bad input), then the detector runs and its issues aggregate. One engine spans every input shape in the family: "text" (the default) coerces string / Buffer to UTF-8 and refuses anything else; "raw" hands the value through so an object-bag or byte-level detector owns its own bad-input (identical to aggregateIssues(detector(input, opts))); a guard with a bespoke shape passes its own extractor function(input) -> { subject } | { badInput: message }. Result ok is true only when no detected issue is critical / high severity. The opts argument is forwarded verbatim as the detector's second argument — its shape is detector-defined.

function detectFormulaTrigger(text) {
  if (/^[=+\-@]/.test(text)) {
    return [{ kind: "csv.formula-injection", severity: "high",
              snippet: text.slice(0, 16) }];
  }
  return [];
}
var bad = b.gateContract.runIssueValidator("=cmd|x", {}, detectFormulaTrigger);
bad.ok;                                              // → false
var ok  = b.gateContract.runIssueValidator("ada,36", {}, detectFormulaTrigger);
ok.ok;                                               // → true

b.gateContract.resolveProfileAndPosture(opts, cfg) #

stable0.7.5hipaapci-dssgdprsoc2
{
  profiles:           object,        // PROFILES table (required)
  compliancePostures: object,        // COMPLIANCE_POSTURES table (required)
  defaults:           object,        // baseline before overlay
  errorClass:         FrameworkError,// throws via .factory(code, msg)
  errCodePrefix:      string,        // e.g. "csv" → "csv.bad-profile"
}

Overlay opts.profile and opts.compliancePosture on top of a defaults object using guard-supplied tables. Every guard primitive's factory routes through this so the resolution shape — defaults first, profile overlay, posture overlay, inline opts last — stays identical across the family. When opts.compliancePosture is unset and b.compliance.set() has declared a global posture, the global posture takes effect (the value-add of the top-level coordinator).

Throws cfg.errorClass.factory(cfg.errCodePrefix + ".bad-profile") for unknown profile names and ... + ".bad-posture" for unknown postures.

var PROFILES = {
  strict:   { formulaInjectionPolicy: "reject",     bidiCharPolicy: "reject" },
  balanced: { formulaInjectionPolicy: "prefix-tab", bidiCharPolicy: "strip"  },
};
var POSTURES = { hipaa: { piiPolicy: "redact" } };
var resolved = b.gateContract.resolveProfileAndPosture(
  { profile: "balanced", compliancePosture: "hipaa", maxCellBytes: 65536 },
  {
    profiles:           PROFILES,
    compliancePostures: POSTURES,
    defaults:           { maxCellBytes: 1024 },
    errorClass:         b.guardCsv.GuardCsvError,
    errCodePrefix:      "csv",
  });
resolved.formulaInjectionPolicy;                     // → "prefix-tab"
resolved.piiPolicy;                                  // → "redact"
resolved.maxCellBytes;                               // → 65536

b.gateContract.buildProfile(opts) #

stable0.7.5
{
  baseProfile:    string,           // start from this profile name
  extends:        string|string[],  // additional base(s) (later-wins)
  overrides:      object,           // inline merge after extends
  removes:        object,           // drop array entries or keys
  resolveProfile: function,         // (name) → profile|null  (required)
}

Recursive profile composition with cycle detection. Walks opts.baseProfile and every name in opts.extends through the caller-supplied opts.resolveProfile resolver, deep-merging arrays (set-union, later-wins on duplicates) and objects (recursive). Then applies inline opts.overrides and finally opts.removes (which can drop array entries or whole keys). Cycles throw gate-contract/profile-cycle; unknown names throw gate-contract/unknown-profile. Most guards bind through makeProfileBuilder and never call buildProfile directly.

var PROFILES = {
  "blog-post": {
    allowedTags: ["p", "a", "strong"],
    allowedAttrs: { a: ["href", "target"] },
  },
  "with-images": { extends: ["blog-post"], allowedTags: ["img"] },
};
var resolved = b.gateContract.buildProfile({
  baseProfile:    "with-images",
  overrides:      { allowedTags: ["em"] },
  removes:        { allowedAttrs: { a: ["target"] } },
  resolveProfile: function (n) { return PROFILES[n] || null; },
});
resolved.allowedTags;                                // → ["p","a","strong","img","em"]
resolved.allowedAttrs.a;                             // → ["href"]

b.gateContract.summarizeIssues(issues) #

stable0.7.5

Project a gate decision's issues array down to the audit-friendly shape — { kind, severity, ruleId } only. Full snippets stay in the forensic evidence store; the audit log records the classification without the offending bytes. Replaces the inline (d.issues || []).map(...) pattern host primitives previously carried per emit site.

var summary = b.gateContract.summarizeIssues([
  { kind: "csv.bidi", severity: "high", ruleId: "BIDI-OVERRIDE",
    snippet: "" },
  { kind: "csv.trailing-whitespace", severity: "info", ruleId: "TRIM" },
]);
summary.length;                                      // → 2
summary[0].snippet;                                  // → undefined  (stripped)
summary[0].ruleId;                                   // → "BIDI-OVERRIDE"

b.gateContract.composeHooks(hooks) #

stable0.7.5

Chain a list of operator hooks into a single async hook. Empty arrays return null (so defineGate can pass the result through its hooks.X || null slot); single-element arrays return the lone hook unchanged. Multi-element chains run sequentially — { suppress: true } or { skip: true } from any hook short- circuits and returns; otherwise the last non-null hook result wins.

var redactPii = function (issue) {
  return Object.assign({}, issue, { snippet: "" });
};
var dropInfo  = function (issue) {
  return issue.severity === "info" ? { suppress: true } : null;
};
var onIssue = b.gateContract.composeHooks([dropInfo, redactPii]);
var infoHit = await onIssue({ kind: "csv.trim", severity: "info" });
infoHit.suppress;                                    // → true
var bidi = await onIssue({ kind: "csv.bidi", severity: "high",
                           snippet: "U+202E" });
bidi.snippet;                                        // → ""

b.gateContract.defineGuard(spec) #

stable0.15.0
{
  name:                 string,     // NAME (e.g. "csv"); required
  kind:                 string,     // "content"|"filename"|"identifier"|"command" for the default gate; any non-empty label with a bespoke spec.gate; required
  errCodePrefix:        string,     // error-code namespace (default name)
  errorName:            string,     // defineClass name (mutually exclusive with errorClass)
  errorClass:           function,   // pre-built FrameworkError subclass
  profiles:             object,     // PROFILES (must include strict/balanced/permissive); required
  defaults:             object,     // DEFAULTS baseline (default profiles.strict, or strictDefaults(profiles, defaultsOverlay) when `base` is given)
  postures:             object,     // COMPLIANCE_POSTURES (default ALL_STRICT_POSTURES, or compliancePostures(profiles, { base }) when `base` is given)
  base:                 number,     // forensic snippet budget — when given (and defaults/postures omitted), the factory derives both via strictDefaults + compliancePostures
  defaultsOverlay:      object,     // per-guard default overrides merged into the derived strictDefaults (e.g. { maxRuntimeMs: ... }); only used with `base`
  mimeTypes:            string[],   // content guards only
  extensions:           string[],   // content guards only
  integrationFixtures:  object,     // INTEGRATION_FIXTURES (consumed by host harness)
  validate:             function,   // (input, resolvedOpts) -> { ok, issues }; required
  sanitize:             function,   // (input, resolvedOpts) -> cleaned (optional)
  gate:                 function,   // (resolvedOpts) -> async (ctx) -> decision (optional; default built per kind)
  ctxFields:            string[],   // ordered ctx field names the default gate reads (overrides the per-KIND table; e.g. ["identifier","cidr"])
  defaultGateCheck:     function,   // override the default gate's per-ctx check
  extra:                object,     // additional exports merged verbatim into module.exports
}

Assemble a complete b.guard* module from a spec. Mints the per-guard error class (via framework-error.defineClass, or accepts a supplied errorClass), wires resolveProfileAndPosture / buildGuardGate / makeProfileBuilder / lookupCompliancePosture / makeRulePackLoader, and returns the frozen module.exports object every guard ships — NAME / KIND / PROFILES / DEFAULTS / COMPLIANCE_POSTURES / INTEGRATION_FIXTURES / validate / sanitize? / gate? / buildProfile / compliancePosture / loadRulePack plus the spec's extra exports (verb tables, escapeCell, schema, kidSafe, …) and the error class under its own name.

The per-guard inspection logic is INJECTED, not abstracted: validate / sanitize / gate are spec functions that close over the resolved opts. A guard whose gate body is the standard serve→audit-only→sanitize→refuse chain can omit spec.gate and take the factory default (built from spec.validate + spec.sanitize per KIND); a guard with a bespoke gate (CSV's sanitize-reparse-reserialize, filename's per-policy canSanitize matrix) passes its own. Behavior is preserved byte-for-byte because the genuinely-divergent code stays verbatim in the spec — the factory only removes the wiring every guard copies.

module.exports = b.gateContract.defineGuard({
  name: "csv", kind: "content", errorClass: GuardCsvError,
  profiles: PROFILES, defaults: DEFAULTS, postures: COMPLIANCE_POSTURES,
  mimeTypes: ["text/csv"], extensions: [".csv"],
  integrationFixtures: INTEGRATION_FIXTURES,
  validate: validate, sanitize: sanitize, gate: gate,
  extra: { serialize: serialize, escapeCell: escapeCell, schema: schema },
});

b.gateContract.defineParser(spec) #

stable0.15.0
{
  name:        string,     // module identity / error-name stem; required
  entry:       function,   // the validate/parse entry point; required
  entryName:   string,     // export key for the entry (default "validate")
  profiles:    object,     // PROFILES; required
  postures:    object,     // COMPLIANCE_POSTURES (default ALL_STRICT_POSTURES)
  errorClass:  function,   // pre-built FrameworkError subclass
  errorName:   string,     // defineClass name (mutually exclusive with errorClass)
  extra:       object,     // additional exports (verb tables, KNOWN_*, …)
}

Assemble the minimal command / line-protocol / safe-* parser module shape — guards whose four compliance postures all resolve to strict (composing ALL_STRICT_POSTURES) and whose surface is a single self-contained validate / parse entry point plus a compliancePosture(name) that returns the effective PROFILE NAME (or null for unknown names) rather than an overlay clone. These guards carry no gate / buildProfile / loadRulePack, so defineGuard's full assembly would be wrong for them.

Mints the error class (or accepts one), exposes the spec's primary entry point under spec.entryName (default "validate"), and returns the frozen module.exports with PROFILES / COMPLIANCE_POSTURES / compliancePosture plus the spec's extra exports and the error class.

module.exports = b.gateContract.defineParser({
  name: "pop3-command", entry: validate,
  errorClass: GuardPop3CommandError,
  profiles: PROFILES, postures: COMPLIANCE_POSTURES,
  extra: { KNOWN_VERBS: KNOWN_VERBS, ZERO_ARG_VERBS: ZERO_ARG_VERBS },
});

Last updated 2026-08-08T16:39:49.652Z by seeder.