Unclassified
Round 2 — Environment and Tenancy Isolation Manifest
Date: 2026-07-29
Status: Code-level isolation exists; live verification needed
Three Runtime Profiles — Isolation Matrix
Dev Internal
| Dimension | Value | Enforcement |
|---|---|---|
| Tenant ID | Local dev tenant (e.g., dev-local) | .env or seed auth |
| Database | Local Neon/Docker Postgres | DATABASE_URL env var |
| Workers | Local via turbo dev | Local process |
| Connectors | Full set, test accounts, sandbox providers | No credential validation |
| Auth | Dev Clerk keys or mock | .env |
| Secrets | Local .env files | Gitignored |
| Routines | Full diagnostic routes, seed tools | Tenant-gated by tenant_id |
| Isolation | Process-level (local machine) | N/A |
Customer Zero
| Dimension | Value | Enforcement |
|---|---|---|
| Tenant ID | iw-customer-zero | Hardcoded check in clerk-auth.ts via isCustomerZeroEmail() |
| Database | Dedicated Neon database or schema | Separate DATABASE_URL binding |
| Workers | Dedicated or tagged Cloudflare Workers | IW_CUSTOMER_ZERO=true env |
| Connectors | Real production connectors, real providers | Full credential set in Secret Store |
| Auth | Clerk production, @integratewise.ai domain | Clerk org iw-customer-zero |
| Secrets | CUSTOMER_ZERO_* namespace in Secret Store | Cloudflare Workers secrets |
| Routines | Full diagnostic routes, internal tools, seed controls | Tenant ID gate |
| Isolation | Tenant-level (dedicated DB, dedicated Workers) | Gateway tenant resolution |
External Product
| Dimension | Value | Enforcement |
|---|---|---|
| Tenant ID | Auto-generated <tenant-slug> | Created at marketplace installation |
| Database | Shared Neon + strict RLS | USING (tenant_id = current_setting('app.tenant_id')::text) |
| Workers | Shared stateless Cloudflare Workers | Same deployment as Customer Zero |
| Connectors | Same binaries; tenant-scoped credentials | Secret Store TENANT_<slug>_* |
| Auth | Clerk production, external domains + marketplace JWT | Clerk org per tenant |
| Secrets | TENANT_<slug>_* namespace in Secret Store | Cloudflare Workers secrets |
| Routines | 19-tool public contract only; no diagnostics | Tenant ID gate + profile check |
| Isolation | Row-level (shared DB, strict RLS) | Database-level enforcement |
Code-Level Isolation Points
1. Gateway Tenant Resolution
File: services/gateway/src/index.ts
// Tenant resolution from verified identity
const tenantId = verifiedPrincipal.tenantId;
// Trusted header overwrite (prevents spoofing)
if (isTrustedOrigin(request)) {
request.headers.set("x-tenant-id", tenantId);
}
2. MCP Connector Tool Gating
File: services/mcp-connector/src/continuity-bridge.ts
const isCustomerZero = actor.tenant_id === "iw-customer-zero";
if (isCustomerZero) {
// 19 public + 4 internal tools
tools: [
...PUBLIC_CONTINUITY_BRIDGE_TOOL_DEFINITIONS,
...INTERNAL_CONTINUITY_BRIDGE_TOOL_DEFINITIONS,
];
} else {
// 19 public tools only
tools: PUBLIC_CONTINUITY_BRIDGE_TOOL_DEFINITIONS;
}
3. Gateway Internal Route Gating
File: services/gateway/src/index.ts
// Internal routes blocked for external tenants
if (!isInternalTenant(tenantId) && isInternalRoute(path)) {
return new Response("Forbidden", { status: 403 });
}
4. Neon Query Tenant Isolation
File: services/spine/src/neon-workbench.ts
// Every Neon query includes tenant_id as first bind parameter
const result = await neon.query("SELECT * FROM workbench WHERE tenant_id = $1", [tenantId]);
5. Frontend Server-Resolved Identity
File: apps/web/src/lib/platform/index.ts
// Frontend calls /api/v1/workspace/discovery first
const discovery = await fetch(`${NEXT_PUBLIC_IW_API_URL}/api/v1/workspace/discovery`);
// Renders from server-resolved identity, not client inference
Verification Checklist
| Check | Command | Expected |
|---|---|---|
| Dev Internal: local DB | DATABASE_URL=... pnpm --filter @integratewise/spine dev | Connects to local DB |
| Customer Zero: dedicated DB | Query Neon: SELECT current_database() | Returns Customer Zero DB name |
| External: RLS enforced | Query Neon as external tenant | Returns only rows matching tenant_id |
| Gateway: tenant isolation | curl -H "x-tenant-id: iw-customer-zero" ... | Returns Customer Zero data |
| Gateway: external tenant | curl -H "x-tenant-id: acme-corp" ... | Returns Acme Corp data only |
| Gateway: cross-tenant denial | curl -H "x-tenant-id: acme-corp" .../iw-customer-zero/... | Returns 403 |
| MCP Connector: tool gating | MCP list_tools as external tenant | Returns 19 tools only |
| MCP Connector: internal tools | MCP list_tools as iw-customer-zero | Returns 23 tools |
| Frontend: no direct DB | Grep apps/web for postgres:// or neon.tech | Zero hits |
| Frontend: no direct provider | Grep apps/web for provider API URLs | Zero hits |