Guard Country

ISO 3166-1 alpha-2 country-code guard. Accepts the 249 officially assigned codes and refuses everything else, from a bundled table. KIND="identifier" - the gate consumes ctx.identifier (or ctx.country / ctx.countryCode).

The framework already routes on these codes: compliance postures are keyed by jurisdiction, and data-residency, tax nexus, geo-restriction and DSR-jurisdiction decisions all turn on one. A two-letter string that looks like a country but is not one produces a wrong answer at every one of those, wearing the shape of a right one.

Threat catalog: not-a-code (UK - the United Kingdom is GB); user-assigned codes (AA, QM-QZ, XA-XZ, ZZ, which includes CLDR's unknown-region sentinel and XK for Kosovo); exceptionally reserved codes (EU, EZ, UN, AC, CP, CQ, DG, EA, IC, TA) that name unions, organizations and territories rather than countries; formerly used codes (AN, BU, CS, DD, FX, NT, SU, TP, YD, YU, ZR) that a legacy dataset still carries; BIDI / zero-width / C0-control / null-byte universal-refuse; and homoglyph or fullwidth spellings that render as a valid code.

The answer comes from the bundled table and never from Intl. A small-icu or no-icu Node build has no Intl.DisplayNames, so the widespread hand-rolled version - which asks DisplayNames whether a code echoes back and returns true from its catch - accepts every two-letter string on those runtimes. That is a deployment-shaped behavior change with no signal.

Profiles: strict / balanced / permissive. Compliance postures: hipaa / pci-dss / gdpr / soc2.

b.guardCountry.validate(input, opts?) #

stable0.18.44hipaapci-dssgdprsoc2
{
  profile:            "strict"|"balanced"|"permissive",
  compliancePosture:  "hipaa"|"pci-dss"|"gdpr"|"soc2",
  bidiPolicy:         "reject"|"audit"|"allow",
  controlPolicy:      "reject"|"audit"|"allow",
  nullBytePolicy:     "reject"|"audit"|"allow",
  zeroWidthPolicy:    "reject"|"audit"|"allow",
  reservedPolicy:     "reject"|"audit"|"allow",
  userAssignedPolicy: "reject"|"audit"|"allow",
  formerlyUsedPolicy: "reject"|"audit"|"allow",
  maxBytes:           number,
}

Inspect a country code against the resolved profile and return { ok, issues }. Each issue carries kind / severity (critical | high | medium | low) / ruleId / snippet. Non-string input returns a single country.bad-input issue rather than throwing - callers that prefer an exception use b.guardCountry.sanitize.

Issue kinds name only what a record can be pointed at: country-user-assigned, country-exceptionally-reserved, country-formerly-used, and country-not-assigned for anything else well-formed. There is deliberately no "unassigned" kind - ISO reserves codes this table has no source for, so the stronger claim would be unsupported.

b.guardCountry.validate("GB", { profile: "strict" }).ok;   // -> true

var uk = b.guardCountry.validate("UK", { profile: "strict" });
uk.ok;                    // -> false
uk.issues[0].ruleId;      // -> "country.country-not-assigned"

b.guardCountry.sanitize(input, opts?) #

stable0.18.44
{
  profile:            "strict"|"balanced"|"permissive",
  compliancePosture:  "hipaa"|"pci-dss"|"gdpr"|"soc2",
  ...:                same shape as b.guardCountry.validate opts,
}

Normalize a country code to its canonical uppercase form and return it. Throws GuardCountryError when any critical or high issue fires - a malformed shape, or a code the resolved profile refuses. Use validate to inspect issues without throwing.

b.guardCountry.sanitize("gb", { profile: "strict" });   // -> "GB"

try {
  b.guardCountry.sanitize("ZZ", { profile: "strict" });
} catch (e) {
  e.code;   // -> "country.country-user-assigned"
}

b.guardCountry.isValid(input) #

stable0.18.44

Whether input is an officially assigned ISO 3166-1 alpha-2 code, as a boolean. Case-insensitive; never throws, so a non-string is false rather than an exception.

Takes NO profile, deliberately. The profiles differ in how they DISPOSE of a non-assigned code — strict refuses it, balanced and permissive downgrade some findings to a warning — and none of them changes whether ISO assigned the code. A predicate that accepted a profile would answer true for EU, ZZ or SU under permissive, which is exactly the reserved-and-sentinel value a residency or jurisdiction decision must never act on.

Reach for validate(input, { profile }) when the profile-dependent disposition is what you want, and for the reason a code was refused.

b.guardCountry.isValid("gb");   // -> true
b.guardCountry.isValid("UK");   // -> false
b.guardCountry.isValid("ZZ");   // -> false
b.guardCountry.isValid(null);   // -> false

b.guardCountry.compliancePosture(name) #

stable0.18.44hipaapci-dssgdprsoc2

Look up a compliance-posture overlay by name (one of "hipaa" / "pci-dss" / "gdpr" / "soc2"). Returns a fresh clone of the posture overlay so the caller may mutate it freely without disturbing the shared table. Throws GuardCountryError with code "country.bad-posture" when the name is not one this guard maps. Wired by gateContract.defineGuard through gateContract.lookupCompliancePosture, so the clone semantics and error code are identical across every guard in the family.

var posture = b.guardCountry.compliancePosture("hipaa");
posture;                                             // → overlay clone (mutable)

try {
  b.guardCountry.compliancePosture("not-a-regime");
} catch (e) {
  e.code;                                            // → "country.bad-posture"
}

b.guardCountry.buildProfile(opts) #

stable0.18.44
{
  extends:   string|string[],   // base profile name(s) to compose
  ...:       any guard key,      // inline override of resolved keys
}

Compose a derived profile from one or more named bases plus inline overrides, resolving names through this guard's own PROFILES table. opts.extends is a base profile name ("strict" / "balanced" / "permissive") or an array of names — later entries shadow earlier ones, and inline opts keys win last. Wired by gateContract.defineGuard through gateContract.makeProfileBuilder, so operator-defined profiles stay traceable to a baseline instead of a hand-typed dictionary.

var custom = b.guardCountry.buildProfile({ extends: "strict" });
custom;                                              // → composed profile object

b.guardCountry.loadRulePack(pack) #

stable0.18.44

Register an operator-supplied rule pack with this guard's rule-pack registry. The pack is identified by pack.id (a non-empty string) and stored for later dispatch by gates that opt in via opts.rulePackId. Returns the pack unchanged on success; throws GuardCountryError with code "country.bad-opt" when pack is missing or pack.id is not a non-empty string. Wired by gateContract.defineGuard through gateContract.makeRulePackLoader, so storage shape and validation are identical across the family.

var pack = b.guardCountry.loadRulePack({ id: "tenant-policy", rules: [] });
pack.id;                                             // → "tenant-policy"

b.guardCountry.gate(opts?) #

stable0.18.44
{
  profile:           string,    // one of PROFILES; default this guard's default
  compliancePosture: string,    // overlay one of hipaa/pci-dss/gdpr/soc2
  mode:              string,    // one of gateContract MODES; default "enforce"
}

Build the guard's request-boundary gate — a contract-shaped object exposing check(ctx) that host primitives call at their byte moment. This is the factory default chain: serve when no issue, audit-only for info / warn issues, and refuse for any high / critical issue, dispatched to the right ctx field by the guard's KIND. Wired by gateContract.defineGuard through gateContract.buildGuardGate; a guard whose gate diverges (a bespoke sanitize-and-reserialize chain, for example) ships its own gate block instead of this template.

var gate = b.guardCountry.gate({ profile: "strict" });
var decision = await gate.check({ bytes: Buffer.from("...") });
decision.action;                                     // → "serve" | "refuse" | …

b.guardCountry.resolveOpts(opts?) #

stable0.18.44
{
  profile:           string,    // one of PROFILES; default this guard's default
  compliancePosture: string,    // overlay one of hipaa/pci-dss/gdpr/soc2
}

Resolve caller opts against this guard's PROFILES + compliance-posture overlays into the fully-defaulted option set the guard runs on — the same resolution validate / sanitize / gate apply internally. Wired by gateContract.defineGuard from the guard's binding config (profiles / postures / defaults / error class), so a guard's bespoke gate calls resolveOpts instead of re-declaring the per-guard resolver wrapper. Throws GuardCountryError with code "country.bad-opt" / "country.bad-posture" on an unknown profile or posture name.

var resolved = b.guardCountry.resolveOpts({ profile: "strict" });
resolved.profile;                                    // → "strict"

Last updated 2026-10-12T14:21:56.879Z by seeder.