P09 Data & State Plane — OSS Survey Notes
Run: 2026-08-27-sprint-1-fable · Lane S1-L4 · 41 rows in top-repos.jsonl · research only, nothing cloned or executed.
Method
Every license claim was resolved today against the live repository, not from memory: fetch LICENSE / LICENSE.md / LICENSE.txt / COPYRIGHT from raw.githubusercontent.com across main and master, and separately query the GitHub API for SPDX id, pushed_at, stars, and archived. Where the two disagreed, the body wins and the disagreement is recorded in the row.
32 of 41 rows are license_verified: "body" — the license text itself was read. The other 9 are "badge": GitHub-reported SPDX for uncontroversial MIT/Apache-2.0 cases showing no split-licensing signal. No row is "unverified". Wherever badge and body could plausibly diverge I fetched the body, and that is where every landmine below was found.
Three checks ran specifically because of the Composio failure this project already absorbed (an MIT badge covering only an SDK to a paid hosted API):
- Per-package overrides under a permissive root.
electric-sql/electric→packages/sync-service/LICENSEindependently Apache-2.0 (holds up);rocicorp/mono→packages/zero/LICENSE404s, so the Apache-2.0 root genuinely governs. - Meta-repo badges that don't cover the runtime.
supabase/supabaseis Apache-2.0, but that badge coversapps/(docs, studio, www). The components a host would run live in separate repos, each checked:supabase/realtimeApache-2.0,supabase/storageApache-2.0,supabase/authMIT,supabase/pg_graphqlApache-2.0. - Permissive license on a repo that isn't the live product.
hasura/graphql-engineis genuinely Apache-2.0, but it's the v2 engine in maintenance (push 2026-08-19); Hasura's current DDN/v3 engine isn't published there under those terms. The license is real, the product it covers isn't the one the vendor sells. Filedconditional, nottop10.
Counts re-derived from the written file immediately before reporting: 41 rows, 10 top10, 22 register, 9 rejected; hosted_embeddable = 27 yes / 6 conditional / 8 no.
License landscape — which categories are mined
Risk isn't evenly spread. It clusters around the components most attractive to embed, because those are exactly the ones whose vendors built a hosted business to protect.
- Read-model/sync — most heavily mined. Two of five disqualify: PowerSync Service is FSL-1.1, Materialize is BSL 1.1. PowerSync's body excludes use that "offers the same or substantially similar functionality as the Software" — a multi-tenant host embedding a sync engine is the described case, not an edge reading. Survivors: ElectricSQL, Zero (Apache-2.0), RisingWave (Apache-2.0).
- Object storage — effectively closed. MinIO is AGPL-3.0 and archived (push 2026-04-24). Garage is AGPL-3.0. SeaweedFS (Apache-2.0, push 2026-08-27) is the only viable S3-compatible option found — a top-10 entry by elimination rather than competition, which is a concentration risk worth naming.
- Event log/streaming — split. Redpanda is BSL 1.1 plus a separate Redpanda Community License, its Additional Use Grant explicitly excluding a "Streaming or Queuing Service." NATS and Debezium (both Apache-2.0) are clean.
- Workflow/queues — one severe trap. Inngest is SSPL v1, whose service clause reaches the surrounding stack. Temporal MIT, River MPL-2.0, pg-boss/graphile-worker MIT, pgmq PostgreSQL License.
- Postgres core/extensions — clean but for one exception. Postgres, pgvector, pg_partman, pgmq, pg_cron all PostgreSQL License. Citus is AGPL-3.0, the most commonly-misremembered license here.
- Search — no clean permissive default. Meilisearch is now dual MIT AND BUSL-1.1; Typesense is GPL-3.0. Only OpenSearch is plainly Apache-2.0, and it's the heaviest of the three.
Five findings that contradict common belief
Rows where reading the body changed the answer, and where reputation would have produced a wrong deliverable:
- Citus is AGPL-3.0 — assumed permissive because Microsoft owns it and it presents as "just an extension." The extension-vs-application linkage question under AGPL is unsettled; a hosted multi-tenant service is not where to test it.
- Liquibase is FSL-1.1, not Apache-2.0 — GitHub reports only "Other"; the body reads "Functional Source License, Version 1.1, ALv2 Future License," © 2025 Liquibase Inc. Institutional memory says Apache and is out of date.
- Redpanda has no license per the GitHub API (
license: null) — any automated scan sees nothing and passes it. Real terms live inlicenses/bsl.mdandlicenses/rcl.md. - Meilisearch is no longer simply MIT — its LICENSE declares
SPDX-License-Identifier: MIT AND BUSL-1.1, with an Enterprise Edition subset under BUSL. Use requires proving no EE feature is in the build path: ongoing diligence, not a one-time check. - MinIO is archived — the AGPL problem is the headline, but the archive status is arguably the bigger operational fact, and it's invisible to a license check alone.
Two "reads clean but isn't simple" cases: TimescaleDB is split, with compression and continuous aggregates — the actual reasons to adopt it — inside tsl/ under a proprietary EULA; Typesense's LICENSE 404s on master because its default branch is v31, so a naive fetch reports no license where GPL-3.0 is the truth.
Top-10 rationale
Selected for verified permissive license, active maintenance, a clear seam, and non-overlapping ownership of one job. Where two candidates owned the same job, the lighter one won.
| Repo | Owns | Why it made the cut |
|---|---|---|
| PostgreSQL | System of record | Everything else is a satellite; PostgreSQL License maximally unencumbered |
| Drizzle ORM | Schema + typed query layer | Works naturally with RLS session variables, the isolation mechanism |
| Atlas | Migration + drift detection | Only surveyed tool answering "does the donor store still match our contract" |
| NATS/JetStream | Event transport + replay | Apache-2.0; subject hierarchies map onto tenant isolation; far lighter than Kafka |
| ElectricSQL | Partial replication to clients | Read-model seam; per-package license independently verified |
| SeaweedFS | S3-compatible artifacts | Only maintained permissive object store left standing |
| Temporal | Durable execution | Installs/migrations must resume after crash, not restart; MIT at this scope is rare |
| pg-boss | Transactional background jobs | Enqueue in the same transaction as the state change — no race, no new infra |
| PostgREST | REST over Postgres w/ RLS authz | Community-owned MIT, no single vendor able to relicense — durability edge over Hasura/Supabase |
| Supabase components | Auth, realtime, storage | Adoptable individually; each component repo verified separately from the meta-repo badge |
Four candidates were demoted from an initial 14 to reach exactly 10, each recorded in its row: pgvector (add-on to the Postgres row, load-bearing only if semantic capability-matching is in scope), Debezium (overlaps NATS on the transport seam, plus JVM + Kafka Connect weight), RisingWave (streaming materialized views are a scale optimization, not a Sprint-1 pillar), DuckDB (analytics is adjacent to the state path).
The empty category: multi-tenancy frameworks
The brief asked for RLS/tenant libraries with real adoption. There are effectively none for a TypeScript host — a finding, not a gap in the search. Three searches ("multi-tenant postgres RLS nodejs", "tenant isolation postgres schema per tenant", "drizzle rls supabase") returned almost entirely sub-5-star starter templates and unrelated SaaS apps. The only maintained frameworks with real adoption are language-locked: archtechx/tenancy (Laravel/PHP, 4.4k stars, push 2026-08-27) and citusdata/django-multitenant (Python, 822 stars). Both are useful as design references for the tenant-context lifecycle; neither is adoptable as a dependency.
Consequence for Sprint-1: tenant isolation is bespoke work Actionist must own and test, not a library to install. Budget it as such — it's the highest-risk correctness surface in the plane.
Innovation candidates
- P09-I-R-1 — Tenant context as a typed Drizzle wrapper that cannot emit an unscoped query: build the missing multi-tenancy library, in types, since none exists to adopt.
- P09-I-R-2 — Atlas drift detection run continuously against donor-owned stores, turning "the donor app changed its schema under us" from an outage into a monitored, alertable event.
- P09-I-R-3 — Transactional outbox on pg-boss: state change and event enqueue in one transaction, eliminating the enqueue-vs-commit race across the federation layer.
- P09-I-R-4 — RLS policies as the single authorization source, with PostgREST and the host service both subject to them, so isolation can't be bypassed by adding a service.
- P09-I-R-5 — Electric shapes as the donor-app read contract: subscribe to a shape rather than polling a bespoke endpoint per integration.
- P09-I-R-6 — Per-tenant libSQL databases for hot runtime state with Postgres as durable registry — database-per-tenant without a schema explosion.
- P09-I-R-7 — Capability catalog as pgvector embeddings plus relational filters in one query, avoiding a separate search service and its license problems entirely.
- P09-I-R-8 — Temporal workflow per app install, making a partially-completed install a resumable object rather than manual cleanup.
- P09-I-R-9 — pg_partman-managed audit partitions with retention encoded as tenant policy, so retention promises are enforced by the database, not a cron script.
- P09-I-R-10 — DuckDB over exported Parquet in SeaweedFS for per-tenant usage reporting, with zero warehouse infrastructure.
- P09-I-R-11 — A license-provenance CI gate failing on AGPL/BSL/SSPL/FSL entering the dependency tree — this survey's method, automated, so the next Composio never lands.
- P09-I-R-12 — NATS subject hierarchy as the event-plane tenant boundary, mirroring the RLS boundary for state so both planes share one mental model.
- P09-I-R-13 — Schema contract versioning between host and donor app, Atlas as diffing engine, explicit compatibility window.
- P09-I-R-14 — Shadow-read verification: serve from Actionist state while comparing against the donor's native store, quantifying federation drift before trusting it.
- P09-I-R-15 — pgmq for polyglot donor runtimes that can't use a Node or Go client, keeping one queue substrate across languages.
- P09-I-R-16 — Supabase Realtime as drop-in change fan-out for donor UIs, adopted as a single component rather than the whole platform.
- P09-I-R-17 — Deferred: RisingWave materialized views for cross-tenant rollups if batch recompute becomes the bottleneck.
Unknowns and blockers
- Not blocking, unresolved: whether the host runtime is TypeScript, Go, or mixed. Several register-tier choices (River vs pg-boss, sqlc vs Drizzle) resolve immediately once fixed.
- Atlas OSS feature boundary is at the vendor's discretion; confirm specific drift-detection capabilities are in the OSS binary before committing to P09-I-R-2.
- Zero (Rocicorp) is pre-1.0 with a moving API at 3.4k stars. Apache-2.0 and genuinely promising, but a bet, not a foundation — flagged rather than recommended.
- Sequin (MIT, clean license, good fit) last pushed 2026-02-23, ~6 months stale while every peer pushed within days. Continuity is an open risk.
- Not verified in this run: transitive dependency licenses for any repo. Top-level licenses are read; the trees underneath are not, and that's where the next surprise lives (see P09-I-R-11).
- Legal review recommended on the three conditional rows (TimescaleDB split, Meilisearch MIT/BUSL dual, Typesense GPL-3.0 at arm's length). My reading of the bodies is in each row, but the Citus extension-linkage question in particular is a legal judgment, not an engineering one.