Build the safeguards
Eight explained steps, a source example and a checkpoint at each decision.
Read the tutorialAPI security / PostgreSQL RLS / Identity
Start in your browser, then take the complete Tenant Boundary Lab into an independent local environment.
Download and extract the ZIP into a new folder. With Python 3 installed, open a terminal in that folder and run the command below. It serves only this package on loopback; opening index.html directly is insufficient for workers and WebAssembly.
python preview.pyOpen http://127.0.0.1:4210/ in your browser. Stop the server with Ctrl+C. The prebuilt preview needs no dependency installation and makes no automatic requests to another site. The source code and notices are included alongside it.
The commands below start in the extracted package root. Node-based examples require Node 24 or later; the Go adapter requires Go 1.27 or later; the Python adapter requires Python 3.11 or later. Install only the toolchain used by this lab. Exact dependency versions and the Node lockfile are in the download.
npm ci
npm test
node src/tenant/server.mjsThe safe cross-company read returns 404 with no foreign rows. A viewer’s edit returns 403; an expired session returns 401. A list query without a tenant filter still returns only Orchard’s row under RLS.
Read GETTING_STARTED.md for the native request examples and shutdown steps. Native servers bind to 127.0.0.1. The optional monitoring containers expose only loopback ports. They are a separate exercise from the browser preview.
Add a second permitted company membership and an explicit company-switch action. Verify membership on the server, set the database context transactionally, and test that a reused connection never retains the previous request’s tenant.
Add the expected result first, reproduce the failure, repair it, then rerun the native and browser checks. Use npm run rebuild for the Node/browser examples. The Go package includes rebuild_wasm.py; the Python package includes refresh_source.py.
Capture the scenario, edition, input records, observed outcome and the test that checks it. Explain one alternative you rejected and why. Avoid converting a small local result into a throughput, security or accuracy claim about a production system.
Mux’s full-stack opening includes identity and account-management product features.
Eight explained steps, a source example and a checkpoint at each decision.
Read the tutorialUnderstand where each pattern helps and which tradeoffs remain.
Compare approachesDownload the independent environment, run the native checks and try a specific extension.
Open the how-to guideLab discussion / Tenant Boundary Lab
Ask about the code, compare an approach, or describe an edge case. Reader comments are reviewed before publication.
Code walkthrough
These short excerpts connect the lab’s behavior to its implementation. Each example links to the full tutorial step for its context, tradeoffs and checkpoint. Names and dependencies belong to that source; the excerpts are not standalone programs.
The expired preset uses an already expired session. The safe policy checks the expiry before querying data. Production identity also needs a trusted issuer or session store, secure transport and revocation rules. Decoding an unverified token or accepting a browser role field would not establish identity.
if (!session || session.expires <= now) return {status: 401};
A viewer may read but not edit. Membership and action checks are separate: being in the right company does not make every action permissible. Deny an unsupported action before changing data, and test that the denial leaves the record unchanged.
if (action === "update" && session.role !== "editor") {
return {allowed: false, status: 403};
}
RLS is ineffective if the application routinely uses a bypassing role. The exercise explicitly changes from the owner to a restricted role before running the query. Policies, grants and owner behavior are different mechanisms. Test the exact role used by the application, including administrative paths.
CREATE ROLE lab_reader NOLOGIN NOSUPERUSER NOBYPASSRLS;
SET ROLE lab_reader;