Two shared wasm-boundary fixes surfaced by the messenger port (WZ-013):
1. uuid[] DbValue variant. bnwasm had no uuid-array bind/scan, so every
ANY($1::uuid[]) query broke in the guest ("unsupported argument type
[]uuid.UUID") and messenger worked around it with a ::text[]::uuid[] cast.
Adds a dedicated DbValue.uuid_array_value (abiv1.UuidArray) — distinct from
text[] so the host binds a native uuid[] param (queries keep ::uuid[]) and
scans a uuid[] column straight into []uuid.UUID. Guest toDbValue marshals
[]uuid.UUID; naturalValue/assign parse the canonical strings back into
[]uuid.UUID (nil→NULL, empty stays empty). Pinned by the uuid_array entry in
the shared DbValueFixtures contract (round-trip + driver-value tests green).
2. Trusted identity headers (auth/trustedheaders.go). Context does not cross
the ABI, so guests cannot see the host's verified principal. The SECURE
contract: the host runs its RBAC guard against the signature-verified JWT,
strips any client-supplied copy of the X-Bn-Verified-* headers, and sets
them itself from auth.Get{Public,}UserFromContext; the guest reconstructs
context via auth.TrustedHeaderMiddleware and trusts ONLY those headers.
Guests MUST NOT decode a client cookie/Bearer token for identity — that is a
privilege-escalation bug (a verified public user forging an admin JWT the
guest would honour on a RolePublic method). Documented in docs/wasm-abi.md,
replacing the ambiguous "auth context reaches the guest via HttpRequest
headers" line that invited the insecure decode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`ninja plugin build` compiles a plugin to reactor-mode wasip1 wasm (Go >= 1.24
enforced), extracts manifest.pb by driving one HOOK_DESCRIBE over wazero with
failing host stubs, and packs a tar.zst .bnp (plugin.wasm, plugin.mod,
manifest.pb + migrations/schemas/assets/web-dist when present) with a summary
table. A describe-time capability call (e.g. db.* from Register) fails with an
actionable error naming the offending method. `ninja plugin verify` re-runs the
CMS reader's layout/name/abi/path-safety/size checks standalone (deliberate
duplication of cms backend/plugin/bnp/reader.go; kept in lockstep by WO-WZ-010).
ABI riders (additive; buf breaking clean):
- ABI_ERROR_CODE_TX_EXPIRED enum value + bnwasm guest mapping to a new
bnwasm.ErrTxExpired sentinel (retryable tx expiry, distinct from real faults);
the cms dbexec side adopts the emit separately.
- PluginManifest.data_dir bool + a first-class `data_dir` key on the plugin.mod
parser (so writeMod's struct round-trip can't drop it); `plugin build` stamps
it from plugin.mod into the manifest.
Docs: wasm-abi.md gains a Building & packing section, the error-code table row,
the manifest data_dir mapping, and the plugin.mod reference. Tests: CLI e2e
builds the WZ-002 fixture → verify + manifest block keys; a capfixture proves
the actionable describe-time error; verify rejects each malformed class;
bnwasm TX_EXPIRED classification.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>