Disciplines · Migration

Breaking Changes

1.

11sections3 minread

On this page

This document tracks breaking changes introduced during the Oshun platform consolidation. Each change includes migration guidance and deprecation timelines.

Table of Contents#

  1. Version Compatibility
  2. API Breaking Changes
  3. SDK Breaking Changes
  4. Event Schema Changes
  5. Database Schema Changes
  6. Authentication Changes
  7. Configuration Changes
  8. Deprecation Schedule

Version Compatibility#

Oshun Version Lilith Compatibility Yemaya Compatibility Migration Required
1.0.0 Breaking Breaking Yes
0.9.x Partial (shim available) Partial (shim available) Recommended
< 0.9.0 Not compatible Not compatible Required

API Breaking Changes#

1. Authentication Header Changes#

Breaking in: Oshun 1.0.0

Before:

http
# Lilith
Authorization: Bearer {lilith_token}
X-Lilith-API-Key: {api_key}

# Yemaya
Authorization: Bearer {yemaya_token}
X-API-Key: {api_key}

After:

http
# All domains use unified format
Authorization: Bearer {oshun_jwt}
# OR
Authorization: ApiKey {oshun_api_key}
# OR
X-API-Key: {oshun_api_key}

Migration:

typescript
// Old
const headers = {
  'X-Lilith-API-Key': process.env.LILITH_API_KEY,
};

// New
const headers = {
  Authorization: `ApiKey ${process.env.OSHUN_API_KEY}`,
};

2. API Base URL Structure#

Breaking in: Oshun 1.0.0

Before:

text
# Lilith
https://api.lilith.io/v1/meditations
https://api.lilith.io/v1/users

# Yemaya
https://api.yemaya.studio/projects
https://api.yemaya.studio/assets

After:

text
# Unified gateway with domain prefixes
https://api.oshun.io/lilith/v1/meditations
https://api.oshun.io/lilith/v1/users

https://api.oshun.io/yemaya/v1/projects
https://api.oshun.io/yemaya/v1/assets

# New domains
https://api.oshun.io/isis/v1/generation
https://api.oshun.io/sophia/v1/search
https://api.oshun.io/hathor/v1/worlds
https://api.oshun.io/bellona/v1/exports

Migration:

typescript
// Old
const LILITH_API = 'https://api.lilith.io/v1';
const response = await fetch(`${LILITH_API}/meditations`);

// New
const OSHUN_API = 'https://api.oshun.io';
const response = await fetch(`${OSHUN_API}/lilith/v1/meditations`);

3. Response Envelope Format#

Breaking in: Oshun 1.0.0

Before (Lilith):

json
{
  "success": true,
  "data": { ... },
  "error": null,
  "meta": { "page": 1, "total": 100 }
}

Before (Yemaya):

json
{
  "result": { ... },
  "status": "ok",
  "pagination": { ... }
}

After (All domains):

json
// Success response
{
  "data": { ... },
  "meta": {
    "pagination": { "page": 1, "pageSize": 20, "total": 100 },
    "requestId": "req_abc123"
  }
}

// Error response
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Meditation not found",
    "details": { "id": "med-123" },
    "requestId": "req_abc123"
  }
}

Migration:

typescript
// Old (Lilith)
const response = await fetch(url);
const { success, data, error } = await response.json();
if (!success) throw new Error(error);

// Old (Yemaya)
const response = await fetch(url);
const { result, status } = await response.json();
if (status !== 'ok') throw new Error('Request failed');

// New (All)
const response = await fetch(url);
const json = await response.json();
if (json.error) {
  throw new ApiError(json.error.code, json.error.message);
}
return json.data;

4. Pagination Parameters#

Breaking in: Oshun 1.0.0

Before:

text
# Lilith
?page=1&limit=20

# Yemaya
?offset=0&count=20

After:

text
# Unified
?page=1&pageSize=20
# OR cursor-based
?cursor=abc123&pageSize=20

Response:

json
{
  "data": [...],
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "total": 100,
      "totalPages": 5,
      "hasNext": true,
      "hasPrev": false,
      "nextCursor": "eyJpZCI6MTAwfQ=="
    }
  }
}

5. Error Code Standardization#

Breaking in: Oshun 1.0.0

Before (Mixed formats):

json
// Lilith
{ "error": "MEDITATION_NOT_FOUND" }
{ "error": { "code": 404, "message": "Not found" } }

// Yemaya
{ "error": "project_not_found" }
{ "status": "error", "message": "Project not found" }

After (Standardized):

json
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "The requested resource was not found",
    "details": {
      "resourceType": "meditation",
      "resourceId": "med-123"
    }
  }
}

Standard Error Codes:

Code HTTP Status Description
VALIDATION_ERROR 400 Request validation failed
INVALID_REQUEST 400 Malformed request
UNAUTHORIZED 401 Authentication required
INVALID_TOKEN 401 Token invalid or expired
FORBIDDEN 403 Permission denied
RESOURCE_NOT_FOUND 404 Resource doesn't exist
CONFLICT 409 Resource conflict
RATE_LIMITED 429 Rate limit exceeded
INTERNAL_ERROR 500 Internal server error
SERVICE_UNAVAILABLE 503 Service temporarily unavailable

SDK Breaking Changes#

1. Client Instantiation#

Breaking in: SDK v2.0.0

Before:

typescript
// Lilith SDK
import { LilithClient } from '@lilith/sdk';
const client = new LilithClient({
  apiKey: 'your-api-key',
  environment: 'production',
});

// Yemaya SDK
import Yemaya from 'yemaya-sdk';
const client = Yemaya.init({
  key: 'your-api-key',
});

After:

typescript
// All SDKs use factory functions
import { createLilithClient } from '@lilith/sdk';
import { createYemayaClient } from '@yemaya/sdk';
import { createIsisClient } from '@isis/client';

const lilith = createLilithClient({
  baseUrl: 'https://api.oshun.io/lilith',
  auth: {
    type: 'api-key',
    credentials: 'your-api-key',
  },
});

const yemaya = createYemayaClient({
  baseUrl: 'https://api.oshun.io/yemaya',
  auth: {
    type: 'api-key',
    credentials: 'your-api-key',
  },
});

2. Method Naming Conventions#

Breaking in: SDK v2.0.0

Before:

typescript
// Lilith - mixed conventions
await client.getMeditation(id);
await client.meditation.fetch(id);
await client.fetchMeditation(id);

// Yemaya - snake_case in some places
await client.get_project(id);
await client.projects.getById(id);

After:

typescript
// Consistent resource.action() pattern
await lilith.meditations.get(id);
await lilith.meditations.list({ page: 1 });
await lilith.meditations.create(data);
await lilith.meditations.update(id, data);
await lilith.meditations.delete(id);

await yemaya.projects.get(id);
await yemaya.projects.list();
await yemaya.projects.create(data);

3. Promise vs Callback API#

Breaking in: SDK v2.0.0

Before:

typescript
// Lilith supported callbacks
client.getMeditation(id, (err, meditation) => {
  if (err) console.error(err);
  else console.log(meditation);
});

After:

typescript
// Promises only (use async/await)
const meditation = await lilith.meditations.get(id);

// For error handling
try {
  const meditation = await lilith.meditations.get(id);
} catch (error) {
  if (error instanceof NotFoundError) {
    // Handle not found
  }
}

4. Type Definitions#

Breaking in: SDK v2.0.0

Before:

typescript
// Loose typing
interface Meditation {
  id: string;
  title: any;
  data: object;
}

After:

typescript
// Strict Zod-validated types
import { z } from 'zod';

export const MeditationSchema = z.object({
  id: z.string().uuid(),
  title: z.string().min(1).max(200),
  description: z.string().optional(),
  duration: z.number().positive(),
  audioUrl: z.string().url().optional(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});

export type Meditation = z.infer<typeof MeditationSchema>;

Event Schema Changes#

1. Event Envelope Format#

Breaking in: Oshun 1.0.0

Before:

json
// Lilith events
{
  "event": "meditation.completed",
  "userId": "user-123",
  "timestamp": 1704067200000
}

// Yemaya events
{
  "type": "project_created",
  "data": { "projectId": "proj-123" },
  "time": "2024-01-15T10:30:00Z"
}

After:

json
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440000",
  "type": "lilith.meditation.completed",
  "source": "lilith",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "correlationId": "req_abc123",
  "payload": {
    "meditationId": "med-123",
    "userId": "user-456",
    "duration": 600
  },
  "metadata": {
    "environment": "production",
    "version": "1.0.0"
  }
}

2. Event Type Naming#

Breaking in: Oshun 1.0.0

Old Event (Lilith) New Event
meditation.completed lilith.meditation.completed
meditation.started lilith.meditation.started
user.achievement lilith.achievement.earned
session.created lilith.session.created
Old Event (Yemaya) New Event
project_created yemaya.project.created
asset_uploaded yemaya.asset.uploaded
export_complete bellona.export.completed
generation_done isis.asset.generated

3. Event Subscription API#

Breaking in: Oshun 1.0.0

Before:

typescript
// Lilith
redis.subscribe('meditation:completed', handler);

// Yemaya
bullMQ.on('completed', handler);

After:

typescript
import { EventBus } from '@oshun/event-bus';

const eventBus = new EventBus({
  redis: { url: process.env.REDIS_URL },
  consumerGroup: 'my-service',
});

// Pattern subscription
eventBus.subscribe('lilith.meditation.*', handler);

// Specific event
eventBus.subscribe('lilith.meditation.completed', handler);

await eventBus.start();

Database Schema Changes#

1. Multi-Schema Architecture#

Breaking in: Oshun 1.0.0

Before: Separate databases per domain

text
lilith-db    → all tables
yemaya-db    → all tables

After: Single cluster, multiple schemas

text
oshun-db
  ├── oshun_auth    → users, sessions, permissions
  ├── lilith        → meditations, progress, achievements
  ├── yemaya        → projects, assets, teams
  ├── isis          → generation_jobs, assets
  ├── sophia        → documents, embeddings
  ├── hathor        → worlds, characters, quests
  └── bellona       → builds, exports, artifacts

Migration:

prisma
// Before
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// After
datasource db {
  provider = "postgresql"
  url      = env("LILITH_DATABASE_URL")
  schemas  = ["lilith"]
}

model Meditation {
  id String @id
  // ...
  @@schema("lilith")
}

2. User ID References#

Breaking in: Oshun 1.0.0

Before:

prisma
model Meditation {
  userId String  // Direct user ID
  user   User    @relation(...)
}

After:

prisma
model Meditation {
  authUserId    String  // Reference to oshun_auth.users.id
  lilithProfile LilithProfile @relation(fields: [authUserId], ...)
}

Migration Script:

sql
-- Add new column
ALTER TABLE lilith.meditation ADD COLUMN auth_user_id UUID;

-- Migrate data
UPDATE lilith.meditation m
SET auth_user_id = (
  SELECT auth_user_id FROM lilith.profile p WHERE p.id = m.user_id
);

-- Drop old column (after verification)
ALTER TABLE lilith.meditation DROP COLUMN user_id;
ALTER TABLE lilith.meditation RENAME COLUMN auth_user_id TO user_id;

3. Timestamp Format#

Breaking in: Oshun 1.0.0

Before:

prisma
model Entity {
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  deletedAt DateTime?  // Optional soft delete
}

After:

prisma
model Entity {
  createdAt DateTime @default(now()) @db.Timestamptz
  updatedAt DateTime @updatedAt @db.Timestamptz
  deletedAt DateTime? @db.Timestamptz
}

All timestamps now use TIMESTAMPTZ (timestamp with timezone) stored in UTC.


Authentication Changes#

1. JWT Claims Structure#

Breaking in: Oshun 1.0.0

Before (Lilith):

json
{
  "sub": "user-123",
  "email": "user@example.com",
  "tier": "premium",
  "exp": 1704067200
}

Before (Yemaya):

json
{
  "userId": "user-123",
  "organizationId": "org-456",
  "role": "admin",
  "exp": 1704067200
}

After (Unified):

json
{
  "sub": "user-123",
  "email": "user@example.com",
  "iss": "https://auth.oshun.io",
  "aud": ["lilith", "yemaya", "isis"],
  "iat": 1704066300,
  "exp": 1704067200,
  "jti": "token-789",
  "org": "org-456",
  "permissions": ["read:meditations", "write:projects"],
  "roles": ["user", "creator"]
}

2. API Key Format#

Breaking in: Oshun 1.0.0

Before:

text
# Lilith
lil_live_abc123...
lil_test_xyz789...

# Yemaya
ym_abc123...

After:

text
# Live keys
sk_live_oshun_abc123...

# Test keys
sk_test_oshun_xyz789...

# Restricted keys (limited scope)
rk_oshun_def456...

3. OAuth Scopes#

Breaking in: Oshun 1.0.0

Before:

text
# Lilith scopes
meditation:read
meditation:write
user:profile

# Yemaya scopes
projects
assets
export

After:

text
# Domain-prefixed scopes
lilith:meditation:read
lilith:meditation:write
lilith:user:read

yemaya:projects:read
yemaya:projects:write
yemaya:assets:read
yemaya:assets:write

isis:generation:read
isis:generation:write

# Wildcard scopes
lilith:*        # All Lilith permissions
yemaya:*:read   # Read all Yemaya resources
*               # Full access (admin)

Configuration Changes#

1. Environment Variables#

Breaking in: Oshun 1.0.0

Old Variable New Variable Notes
LILITH_DATABASE_URL LILITH_DATABASE_URL Unchanged
YEMAYA_DB_URL YEMAYA_DATABASE_URL Renamed
REDIS_HOST, REDIS_PORT REDIS_URL Combined
S3_BUCKET, S3_REGION STORAGE_URL S3-compatible URL
OPENAI_API_KEY ISIS_OPENAI_API_KEY Domain-prefixed
JWT_SECRET AUTH_JWT_PUBLIC_KEY RSA instead of symmetric

Full Environment Template:

bash
# Core
NODE_ENV=production
LOG_LEVEL=info

# Databases (per domain)
LILITH_DATABASE_URL=postgresql://...?schema=lilith
YEMAYA_DATABASE_URL=postgresql://...?schema=yemaya
ISIS_DATABASE_URL=postgresql://...?schema=isis
# ... etc

# Shared Infrastructure
REDIS_URL=redis://localhost:6379
STORAGE_URL=s3://oshun-assets.s3.us-east-1.amazonaws.com

# Authentication
AUTH_JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..."
AUTH_ISSUER=https://auth.oshun.io

# AI Providers (managed by Isis)
ISIS_OPENAI_API_KEY=sk-...
ISIS_ANTHROPIC_API_KEY=sk-ant-...
ISIS_REPLICATE_API_TOKEN=r8_...

# Observability
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.oshun.io
SENTRY_DSN=https://...@sentry.io/...

2. Configuration File Format#

Breaking in: Oshun 1.0.0

Before: Mixed formats

yaml
# lilith-config.yaml
database:
  host: localhost
  port: 5432

# yemaya.config.json
{
  "storage": {
    "bucket": "yemaya-assets"
  }
}

After: Unified config structure

typescript
// config/lilith.config.ts
import { defineConfig } from '@oshun/config';

export default defineConfig({
  domain: 'lilith',
  database: {
    url: process.env.LILITH_DATABASE_URL,
    schema: 'lilith',
  },
  redis: {
    url: process.env.REDIS_URL,
  },
  // Type-safe configuration
});

Deprecation Schedule#

Immediate (Oshun 1.0.0)#

Feature Status Removal
Lilith standalone API Deprecated v2.0.0
Yemaya standalone API Deprecated v2.0.0
Legacy event formats Deprecated v1.5.0
Callback-based SDK methods Removed N/A

Oshun 1.5.0 (Q2 2024)#

Feature Status Removal
Legacy event formats Removed N/A
Old API response format shim Deprecated v2.0.0
Symmetric JWT validation Deprecated v2.0.0

Oshun 2.0.0 (Q4 2024)#

Feature Status Removal
Standalone domain APIs Removed N/A
Old API response format shim Removed N/A
Non-schema database connections Removed N/A
Legacy API key formats Removed N/A

Compatibility Shims#

For gradual migration, compatibility shims are available:

Response Format Shim#

typescript
import { legacyResponseMiddleware } from '@oshun/compat';

// Converts new format to legacy format for old clients
app.use(
  '/api/v1/*',
  legacyResponseMiddleware({
    format: 'lilith', // or 'yemaya'
  })
);

Event Bridge#

typescript
import { EventBridge } from '@oshun/compat';

// Translates old event names to new format
const bridge = new EventBridge({
  mappings: {
    'meditation:completed': 'lilith.meditation.completed',
    project_created: 'yemaya.project.created',
  },
});

await bridge.start();

API Key Translation#

typescript
import { translateApiKey } from '@oshun/compat';

// Converts old format keys to new format
const newKey = translateApiKey('lil_live_abc123');
// Returns: sk_live_oshun_abc123

Getting Help#

  • Migration Support: #oshun-migration on Slack
  • Breaking Changes Questions: Create GitHub Issue with breaking-change label
  • Compatibility Shims: See /libs/compat/ for source code