Working with SDK
Adding modules to @olympusoss/sdk
The SDK is the shared library for Athena, Hera, and Site. Eight source files in src/, 41 exported symbols. Read end-to-end in an afternoon.
What's in scope for the SDK
Things every app needs that don't fit elsewhere:
- Settings vault (key/value in
olympusDB). - Encryption (AES-256-GCM + HKDF).
- TTL cache for hot reads.
- Brute-force protection (login attempts, lockouts, audit log).
- Session location tracking.
Things not in scope:
- HTTP clients to Kratos / Hydra, those live in each app's
services/layer. - React components, those live in Canvas.
- Business logic, those live in the apps.
If you're tempted to add something to the SDK, ask: would I want this in every app, with no app-specific variations? If no, keep it in the app.
Adding a module
cd sdk
touch src/audit-trail.tssrc/audit-trail.ts:
import { db } from "./db";
export interface AuditTrailEntry {
identity_id: string;
event: string;
metadata?: Record<string, unknown>;
}
export async function recordAuditEvent(entry: AuditTrailEntry): Promise<void> {
await db.query(
"INSERT INTO audit_trail (identity_id, event, metadata) VALUES ($1, $2, $3)",
[entry.identity_id, entry.event, entry.metadata ?? {}],
);
}Export from src/index.ts:
export { recordAuditEvent, type AuditTrailEntry } from "./audit-trail";Add a test:
// src/audit-trail.test.ts
import { describe, test, expect } from "vitest";
import { recordAuditEvent } from "./audit-trail";
describe("recordAuditEvent", () => {
test("inserts a row", async () => {
// mock db.query
// ...
});
});Database migrations
The SDK includes auto-migration, on startup, it ensures the olympus database has the expected tables. New tables go in src/db.ts:
const MIGRATIONS = [
// existing
`CREATE TABLE IF NOT EXISTS audit_trail (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
identity_id UUID NOT NULL,
event TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);`,
`CREATE INDEX IF NOT EXISTS audit_trail_identity_idx ON audit_trail (identity_id, created_at DESC);`,
];Migrations are append-only. Don't edit existing entries; add new ones.
Encryption module
Anything sensitive going into the settings vault should be encrypted. The crypto.ts module is the only correct path:
import { encrypt, decrypt } from "@olympusoss/sdk";
const ciphertext = encrypt("secret_value", "record_id");
// ciphertext is "v2:<base64-nonce><base64-ciphertext>"
const plaintext = decrypt(ciphertext, "record_id");The record_id becomes part of the HKDF derivation, same plaintext encrypted twice with different record_ids produces different ciphertexts. See ADR 0006.
Format versioning
Encrypted values are prefixed with v2:. To roll forward to a new encryption format:
- Implement v3 alongside v2.
- Ship the new code: SDK decrypts both v2 and v3, encrypts new values as v3.
- Run the migration script (Operate, Encryption key rotation) to upgrade existing v2 rows to v3.
- Once everything is v3, deprecate v2 decode in a future SDK major version.
Versioning
SDK follows strict semver. Add a changeset:
bun changesetAdding a new export: minor bump. Removing or changing a signature: major.
Linking for live development
cd Olympus/sdk
bun link
cd ../athena
bun link @olympusoss/sdkLocal changes in sdk/src/ are immediately visible in Athena.