Tenant isolation
Slipway is multi-tenant: many organisations share one control plane. Isolation is enforced in layers so that a bug in one layer does not become a cross-tenant leak.
Row-level security
Section titled “Row-level security”Every org-scoped table has an org_id column and an RLS policy:
ALTER TABLE runs ENABLE ROW LEVEL SECURITY;ALTER TABLE runs FORCE ROW LEVEL SECURITY;CREATE POLICY runs_org_isolation ON runs USING (org_id = current_org_id()) -- current_setting('app.org_id', true) WITH CHECK (org_id = current_org_id());Every request that touches org data runs inside a transaction that first executes SET LOCAL app.org_id = '<org>', derived from the authenticated principal, never from the request body. If the setting is absent the policy matches nothing, so a forgotten context returns no rows rather than all of them.
An unprivileged application role
Section titled “An unprivileged application role”The application connects as slipway_app, a role without BYPASSRLS and which is not the table owner. Migrations run as the schema owner (SLIPWAY_DATABASE_MIGRATE_URL) and then grant DML to the app role. Without this split RLS would be silently bypassed, so the separation is part of the supported configuration.
Typed, parameterised queries
Section titled “Typed, parameterised queries”All queries are generated with sqlc and are parameterised. There is no string-built SQL. The query layer takes an org-scoped transaction, so even queries that forget a predicate are filtered by the policy.
Keys per organisation
Section titled “Keys per organisation”Secrets are encrypted with per-secret data keys wrapped by a per-org key-encryption key. Compromising one org’s key material does not decrypt another’s secrets. With BYOK you hold the KEK, and revoking it makes the org’s secrets unreadable to Slipway.
Objects and blobs
Section titled “Objects and blobs”Logs, artifacts and caches are stored under org-prefixed keys and are served only through the API after the same authorisation checks, using short-lived presigned URLs where direct transfer is needed.
Compute isolation
Section titled “Compute isolation”Hosted runners are never shared across organisations. Self-hosted runner pools belong to one org (or project). Jobs are isolated from each other by the executor. See Runner security.
What to test
Section titled “What to test”If you operate Slipway yourself, you can verify isolation directly:
-- as the application role, without org context: expect zero rowsSELECT count(*) FROM runs;
-- with a context: only that org's rowsBEGIN; SET LOCAL app.org_id = 'org_a'; SELECT count(*) FROM runs; ROLLBACK;If the first query returns rows, the application is connecting as a role that bypasses RLS (a superuser or table owner). Fix the connection role before going live.