This document is intended for InfoSec teams evaluating DocsCI for enterprise deployment. It covers the data flow architecture, runner isolation controls, Row-Level Security policy summary, and compliance posture. For a one-page summary, see the SOC 2 section or email security@snippetci.com.
The diagram below shows every system boundary, data flow, and trust crossing in a DocsCI run. Node labels (A–H) correspond to the flow table. The single external trust boundary (D→E) is where the runner calls the customer's staging API — the only outbound call that crosses your network perimeter.
| From | To | Data / Protocol | Trust boundary? |
|---|---|---|---|
| A | B | HTTPS — docs archive + repo metadata (no source code) | Internal |
| B | C | Job enqueue — org_id scoped, RLS-enforced | Internal |
| C | D | Ephemeral job dispatch — credentials never logged | Internal |
| D | E | HTTP — allowlisted host, ephemeral token, 10s timeout | External |
| D | F | Run report — findings + metadata only (no docs content) | Internal |
| F | G | Findings read — org_id gated | Internal |
| G | A | HTTPS — PR comment to GitHub/GitLab (write:pr only) | Internal |
| F | H | Findings read — RLS: org_id + role check | Internal |
Code examples are executed in a five-layer isolation stack. Each layer adds independent controls — a vulnerability in L1 (V8 isolate escape) would still be blocked by L3 (network allowlist) and L4 (Docker seccomp). The layers are described below from innermost to outermost.
All tables use Supabase Row-Level Security (RLS) with ALTER TABLE ... ENABLE ROW LEVEL SECURITY and FORCE ROW LEVEL SECURITY. The Supabase service role (used only by admin migrations) bypasses RLS; the anon and authenticated roles do not. Every application query goes through the authenticated role.
organizations2 policies| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own org | owner, admin, member, viewer | auth.uid() IN (SELECT user_id FROM memberships WHERE org_id = id) | — |
| UPDATE own org | owner, admin | auth.uid() IN (SELECT user_id FROM memberships WHERE org_id = id AND role IN ('owner','admin')) | — |
projects3 policies| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own project | all org members | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid()) | — |
| INSERT project | owner, admin | — | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role IN ('owner','admin')) |
| DELETE project | owner | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role = 'owner') | — |
runs2 policies| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own runs | all org members | project_id IN (SELECT id FROM projects WHERE org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid())) | — |
| INSERT run | owner, admin, member | — | project_id IN (SELECT id FROM projects WHERE org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role != 'viewer')) |
findings1 policy| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own findings | all org members | run_id IN (SELECT id FROM runs WHERE project_id IN (SELECT id FROM projects WHERE org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid()))) | — |
memberships3 policies| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own membership | self + owner/admin | user_id = auth.uid() OR org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role IN ('owner','admin')) | — |
| INSERT membership (invite) | owner, admin | — | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role IN ('owner','admin')) |
| DELETE membership | self (leave) or owner (remove) | user_id = auth.uid() OR org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role = 'owner') | — |
api_tokens2 policies| Policy | Allowed roles | USING (row filter) | WITH CHECK (insert filter) |
|---|---|---|---|
| SELECT own tokens | owner, admin | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role IN ('owner','admin')) | — |
| DELETE token | owner, admin | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid() AND role IN ('owner','admin')) | — |
org_id column on every table is always checked against the authenticated user's memberships. A user belonging to Org A cannot read, write, or enumerate any rows belonging to Org B — even with a valid JWT. The check happens at the database layer, not the application layer.DocsCI is currently undergoing SOC 2 Type II readiness assessment with an AICPA-registered auditing firm. Our observation period begins Q3 2025, with an estimated report delivery date of Q1 2026. In the interim, we provide this security packet and are happy to answer vendor questionnaires directly.
DocsCI conducts annual third-party penetration tests of the API, runner sandbox, and dashboard. The most recent test was conducted in Q1 2025 by an independent security firm. Findings were remediated before publication of this packet. Summary findings available under NDA to enterprise customers.
We follow a coordinated disclosure policy. If you discover a security vulnerability in DocsCI, please report it to security@snippetci.com. We will acknowledge receipt within 24 hours and provide a timeline for remediation within 72 hours. We ask for 90 days to remediate before public disclosure.
PGP key available at https://snippetci.com/.well-known/security.txt. We do not pursue legal action against good-faith security researchers.
We can provide: architecture walkthrough call, vendor questionnaire response, NDA + pen test executive summary, DPA / BAA template, and customer-hosted runner deployment guide.