Migration Guide: Flat Tenant to Enterprise Org Model
This guide explains how to upgrade from the flat tenant model to the enterprise organization model.
Overview
The existing flat model uses tenant_id as the sole isolation boundary. The enterprise model adds organizations, teams, RBAC, namespaces, and policies on top of tenants without breaking existing functionality.
What Changes
| Before | After |
|---|---|
| Tenant is the only boundary | Tenant contains one or more organizations |
| No roles beyond API key permissions (read/write) | 6 org roles + 4 team roles |
| All memories in a flat pool | Memories can be scoped to namespaces |
| No access policies | Policy-driven access control |
| No audit trail | Full audit + access logging |
What Stays the Same
- All existing API routes continue to work unchanged
- Existing API keys remain valid
- Tenant-scoped queries still work (tenant_id filtering is untouched)
POST /v1/capture,POST /v1/learn,POST /v1/synthesizework identically when noX-Organization-IDheader is sent
Migration Steps
Step 1: Run the database migrations
# From the cloud-api directory
psql $DATABASE_URL -f migrations/007_enterprise_org_model.sql
psql $DATABASE_URL -f migrations/008_memory_management.sql
These migrations:
- Create 11 new tables
- Add nullable
organization_idandnamespace_idcolumns tosemantic_memories,events, andcompiled_artifacts - Create 32 indexes
- Are fully additive (no existing data is modified or deleted)
Step 2: Verify existing functionality
After running migrations, verify that your existing API calls still work:
# Should work exactly as before
curl -X POST https://api.hippocortex.dev/v1/capture \
-H "Authorization: Bearer hx_live_..." \
-H "Content-Type: application/json" \
-d '{ "type": "test", "sessionId": "s1", "payload": {} }'
Step 3: Create your first organization
curl -X POST https://api.hippocortex.dev/v1/organizations \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "name": "My Organization" }'
Step 4: Start using enterprise features
Once the org is created, you can:
- Invite members
- Create teams
- Set up namespaces
- Configure policies
- Start sending
X-Organization-IDheader with requests to enable scoped retrieval
Step 5: (Optional) Assign existing memories to namespaces
If you want to scope existing memories into namespaces, you can update them directly in the database:
-- Assign all existing memories for a tenant to a namespace
UPDATE semantic_memories
SET namespace_id = 'ns_xxx', organization_id = 'org_xxx'
WHERE tenant_id = 'ten_xxx';
UPDATE events
SET namespace_id = 'ns_xxx', organization_id = 'org_xxx'
WHERE tenant_id = 'ten_xxx';
This is optional. Unassigned memories continue to work with standard tenant-scoped queries.
Backward Compatibility
The enterprise features are designed to be fully backward compatible:
- No breaking changes -- all existing routes, request formats, and response formats are unchanged
- Opt-in -- enterprise features only activate when
X-Organization-IDis sent - Nullable columns -- all new columns on existing tables are nullable
- Conditional routes -- enterprise routes only mount if the org repo is provided
- Gradual adoption -- you can use enterprise features for some requests and standard features for others
Rollback Plan
If you need to roll back:
- The enterprise routes are conditionally mounted, so they can be disabled by not providing org repos
- New tables can be dropped without affecting existing tables
- New columns on existing tables are nullable, so they can be ignored
-- To fully roll back (destructive):
DROP TABLE IF EXISTS lifecycle_policies CASCADE;
DROP TABLE IF EXISTS memory_lineage CASCADE;
DROP TABLE IF EXISTS memory_access_logs CASCADE;
DROP TABLE IF EXISTS audit_logs CASCADE;
DROP TABLE IF EXISTS memory_policies CASCADE;
DROP TABLE IF EXISTS memory_namespaces CASCADE;
DROP TABLE IF EXISTS agent_identities CASCADE;
DROP TABLE IF EXISTS team_memberships CASCADE;
DROP TABLE IF EXISTS org_memberships CASCADE;
DROP TABLE IF EXISTS teams CASCADE;
DROP TABLE IF EXISTS organizations CASCADE;
ALTER TABLE semantic_memories DROP COLUMN IF EXISTS organization_id;
ALTER TABLE semantic_memories DROP COLUMN IF EXISTS namespace_id;
ALTER TABLE events DROP COLUMN IF EXISTS organization_id;
ALTER TABLE events DROP COLUMN IF EXISTS namespace_id;
ALTER TABLE compiled_artifacts DROP COLUMN IF EXISTS organization_id;
ALTER TABLE compiled_artifacts DROP COLUMN IF EXISTS namespace_id;