Shared Database Architecture

Every ARK product reads and writes to the same Supabase project. This page explains how that works under the hood — what's shared, what's isolated, and why the architecture is set up this way.

You don't need to read this to install or use ARK products. It's here for buyers who want to understand the system, customize it with AI, or build their own integrations on top of it.

The integration model in one diagram

                    Your single Supabase project
   ┌──────────────────────────────────────────────────────────┐
   │                                                          │
   │   Shared platform tables (no prefix)                     │
   │   ┌─────────────┐  ┌────────────┐  ┌─────────────┐       │
   │   │  profiles   │  │   roles    │  │ audit_log   │       │
   │   └─────────────┘  └────────────┘  └─────────────┘       │
   │                                                          │
   │   Product-owned tables (prefixed)                        │
   │   ┌─────────────────┐  ┌──────────────────┐              │
   │   │ track_boards    │  │ comms_channels   │              │
   │   │ track_contacts  │  │ comms_messages   │   ...        │
   │   │ track_groups    │  │ comms_threads    │              │
   │   └─────────────────┘  └──────────────────┘              │
   │                                                          │
   └──────────────────────────────────────────────────────────┘
              ▲                ▲              ▲
              │                │              │
        ┌─────┴─────┐    ┌─────┴─────┐  ┌────┴──────┐
        │  Track    │    │  Comms    │  │   Ink     │
        │ (Vercel)  │    │ (Vercel)  │  │ (Vercel)  │
        └───────────┘    └───────────┘  └───────────┘

Each product is a separate Vercel deployment with its own license key, but they all connect to the same Supabase project using the same URL and anon key.

What's shared vs. what's isolated

Shared (no prefix)

These tables hold data that doesn't belong to any one product:

TableWhat's in it
profilesOne row per user. Email, name, avatar, role. Created when a user signs up; reused by every product.
rolesThe role catalog: admin, member, guest, plus any custom roles you add.
permissionsWhat each role can do (read, write, delete) on each resource type.
teamsTop-level grouping. Most deployments have one team; larger deployments may have several (e.g., per department).
team_membersWho belongs to which team, with what role.
audit_logAppend-only log of significant actions across all products.

Product-owned (prefixed)

Each product gets a prefix, and every table that product owns starts with that prefix:

  • Track: track_* — boards, contacts, groups, conversations, automations.
  • Comms: comms_* — channels, messages, threads, mail, drafts.
  • Pulse: pulse_* — standups, polls, schedules, responses.
  • Ink: ink_* — spaces, documents, blocks, tags.

Product-owned tables can reference shared tables — for example, track_contacts has an owner_profile_id that links to profiles.id. They typically don't reference other products' tables: Track has no foreign key to comms_messages. The cross- product integrations work by reading, not by referencing — Comms can fetch Track contacts via the API, but it doesn't have a hard schema dependency on Track being installed.

Why a single Supabase project

There are three alternative architectures we considered and rejected.

Alternative 1: One Supabase project per product

Pros: hard isolation, schema independence.

Cons:

  • Cross-product integrations are impossible (Comms can't see Track contacts).
  • Users sign up separately in each product.
  • Permissions diverge between products.
  • N projects to back up, monitor, and pay for.

We rejected this. Integrations are core to the ARK pitch.

Alternative 2: One Supabase project, but each product has its own public schema

Pros: cleanest namespacing.

Cons:

  • Postgres schemas don't play well with Supabase's RLS UI tools.
  • Cross-schema foreign keys exist but require extra grants and break some Supabase client behaviors.
  • Schema migration tooling assumes a single schema.

We rejected this for tooling friction.

Alternative 3 (chosen): One Supabase project, one public schema, table prefixes

Pros:

  • Standard Supabase tooling works without special configuration.
  • Foreign keys cross product boundaries naturally.
  • The prefix makes the namespacing explicit at the table name.

Cons:

  • Two products can't both use the same unprefixed table name (low risk; we've reserved short prefixes).

This is what ARK uses.

Row Level Security (RLS) is the security boundary

Every product table has Row Level Security enabled. Policies are scoped by the profile_id of the requesting user (and, when relevant, by team membership).

Example RLS policy on track_contacts:

create policy "Users can read contacts in their teams"
  on track_contacts for select
  using (
    team_id in (
      select team_id from team_members where profile_id = auth.uid()
    )
  );

This means:

  • A user in Team A cannot read Team B's contacts, even though both teams' contacts are in the same table.
  • A user from another product (e.g., Comms) reading a contact via the cross-product integration is governed by the same policy — they can't see anything they wouldn't see directly in Track.

For more on the security model, see Security Model.

Schema migrations

Each ARK product ships its schema.sql as the authoritative source. When we release an update, the GitHub Pull Request includes a migration file under supabase/migrations/ that the buyer runs in the SQL Editor (similar to the original install).

Migrations are:

  • Idempotent — re-running one is safe.
  • Forward-only — we don't ship down migrations. To roll back, restore from a Supabase backup.
  • Schema-only by default — migrations don't touch your data unless explicitly noted in the PR description.

The platform-shared tables (profiles, roles, etc.) are owned by whichever ARK product was installed first; later products' migration files detect existing platform tables and skip those parts.

Backups and exports

Because everything lives in one Supabase project, one backup strategy covers everything.

  • Free tier: daily automated backups, 7-day retention.
  • Pro tier: point-in-time recovery up to 7 days, 14-day daily backups.
  • Manual export: Supabase Dashboard → DatabaseBackupsDownload backup.

For data exports (e.g., to a data warehouse), use Supabase's PostgREST API or connect directly via the Postgres connection string. Your data is your data — there are no export restrictions.

Building your own integrations

Because the database is yours, you can write to it from anywhere:

  • A custom integration script using the Supabase JS SDK with the service-role key.
  • A Zapier or n8n workflow that hits the Supabase REST API.
  • A separate internal app that joins ARK data with your own data.

The convention we recommend: prefix any tables you add with something distinct (e.g., your company initials), so they don't collide with future ARK products. For example, if Acme Corp adds their own table, use acme_invoices, not invoices.

See also