fix(contrail): make community.provision work end-to-end (env-gate + resolver + bridge body)#586
Merged
Merged
Conversation
…ision allowProvisioning was hardcoded false; gate it on CONTRAIL_ALLOW_PROVISIONING === 'true' so the Step-3 one-shot provision window can open without a code edit, then close by unsetting it. Strict equality keeps the route's default-deny posture (403 ProvisioningDisabled) for any non-'true' value. Tests cover unset, "true", and a non-'true' value.
The /xrpc middleware in main.ts runs before Nest's body parsers and never calls next() for net.openmeet.*, so the Express req is an unconsumed readable stream. It built the forwarded fetch Request with method + headers but no body, so every POST XRPC reached the Contrail Hono handler empty and failed required-fields validation. GET (getHealth) has no body, which is why the Step-2 GET-only e2e never caught it. Buffer the raw req stream for non-GET/HEAD and forward it as the body (dropping content-length so undici recomputes it). Unblocks all POST XRPC — provision, putRecord, space.grant — not just provision. Adds an e2e regression guard: an authenticated community.provision call with a full body + bad invite must reach createAccount (502 ProvisioningFailed), not the empty-body "...required" 400. Verified it fails without the fix.
The spaces-authority verifier (buildVerifier in contrail-base) and the
community adopt/identity paths default to a public plc.directory resolver, so
caller DIDs minted on a non-public PLC (the devnet PLC) fail to resolve and
auth 401s with "failed to retrieve did document". Spread the internal-PLC
resolver into spaces.authority and community when CONTRAIL_PLC_URL is set;
the vendor default (public PLC) holds otherwise.
Also inject the CONTRAIL_* community/provision vars straight from .env via
env_file in docker-compose-dev.yml instead of `environment:` ${VAR:-}
overrides that blanked them when absent from the shell.
…ars) The body-forwarding e2e 401'd in CI because the caller DID (minted on the CI devnet PLC) couldn't resolve — CONTRAIL_PLC_URL was unset, so the resolver defaulted to public plc.directory. Provisioning was also disabled, so even past auth it would 403 before reaching createAccount. Set the three provision-window vars in env-example-relational-ci (CONTRAIL_PLC_URL=http://plc:2582, CONTRAIL_ALLOWED_PDS_ENDPOINTS=http://pds:3000, CONTRAIL_ALLOW_PROVISIONING=true) so the test runs end-to-end in CI rather than skipping. aud alignment is automatic: OM's did.json `id` and contrail's verifier serviceDid both default to did:web:api.openmeet.net when SERVICE_DID is unset (as in CI). The test uses a bad invite, so it fails at createAccount and provisions nothing — no accumulating CI state. Also widen the test's gate to require those vars (the real preconditions) so it skips cleanly anywhere the provision window is closed instead of failing with a 401/403.
"Window" implied a time-bounded open/close; these are just config flags that enable provisioning. Rename describeIfProvisionWindow → describeIfProvisioningEnabled and reword the comments/env accordingly. No behavior change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Three openmeet-api fixes that, together, make
POST /xrpc/net.openmeet.community.provisionwork end-to-end against a live PDS+PLC. Discovered while rehearsing Track D Phase 1 Step 3 (one-shot LKF community provision) on the local devnet. The prior plan assumed Step 3 needed only the env-gate; driving it end-to-end surfaced two more real bugs.allowProvisioning(d909095) —CONTRAIL_ALLOW_PROVISIONING === 'true'lifts the operator gate (was hardcodedfalse). Default-deny.c4d4be5) — the/xrpcmiddleware inmain.tsruns before Nest's body parsers and nevernext()s fornet.openmeet.*, soreqis an unconsumed stream. It forwardedmethod+headersbut no body, so every POST XRPC (provision,putRecord,space.grant, …) reached the Hono handler empty and failed required-fields validation. GET (getHealth) has no body, so the Step-2 GET-only e2e never caught it. Fix buffers the stream for non-GET/HEAD and forwards it asbody(droppingcontent-lengthso undici recomputes). Unblocks all of atmo's future write path, not just provision.3e91643) — the spaces-authority verifier (buildVerifierin contrail-base) and community identity paths default to a publicplc.directoryresolver, so caller DIDs minted on a non-public PLC fail to resolve → auth 401 "failed to retrieve did document". Spreads the internal-PLC resolver intospaces.authority+communitywhenCONTRAIL_PLC_URLis set. Also injects theCONTRAIL_*vars from.envviaenv_filein dev compose (was${VAR:-}overrides that blanked them).Test plan
test/contrail/contrail-xrpc.e2e-spec.ts): an authenticatedcommunity.provisioncall with a full body + bad invite must reachcreateAccount(502ProvisioningFailed), not the empty-body"...required"400. Reaching createAccount proves all five fields crossed the bridge; the bad invite fails before any PLC op, so no community is provisioned (repeatable, no accumulating state). Gated on the community env + a live PDS (PDS_URL+PDS_INVITE_CODE); skips cleanly otherwise.main.tsfix → recompile → test goes RED on the exact"...required"message → restored → green. The e2e also implicitly covers fix chore: added tenantId middleware. tenantId available in request. adde… #3 (the caller JWT is internal-PLC-minted, so reaching createAccount means the resolver resolved it).{status:"activated"}, communityDiddid:plc:rdbq2wjapfjmiznsklldbwev; PLC log last-oprotationKeys[0]== the caller'sdid:key(sovereignty invariant holds, Contrail's subordinate keys at [1]/[2]).contrail-xrpce2e suite 6/6 green; lint clean.Notes
@atmo-dev/contrail*fork change needed.pdsEndpoint= public PDS hostname per Decision 11).