Olympus Docs
Develop

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 olympus DB).
  • 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.ts

src/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:

  1. Implement v3 alongside v2.
  2. Ship the new code: SDK decrypts both v2 and v3, encrypts new values as v3.
  3. Run the migration script (Operate, Encryption key rotation) to upgrade existing v2 rows to v3.
  4. Once everything is v3, deprecate v2 decode in a future SDK major version.

Versioning

SDK follows strict semver. Add a changeset:

bun changeset

Adding 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/sdk

Local changes in sdk/src/ are immediately visible in Athena.

On this page