SDK Overview
Architecture of the SeptemCore SDK ecosystem: sdk-core as the shared foundation, 8 domain SDKs, 10 frontend packages, the kernel.primitive() pattern, frontend hooks vs imperative API, sdk-testing, and sdk-codegen.
The SeptemCore SDK is a monorepo of TypeScript packages that gives module developers a single, consistent interface to all 7 platform primitives. Every package follows the same design — imperative calls for backend interactions, React hooks for reactive UI state, and a shared HTTP client with automatic error handling.
Package Ecosystem
12 SDK packages
| Package | npm name | Primary backend |
|---|---|---|
| sdk-core | @platform/sdk-core | API Gateway |
| sdk-auth | @platform/sdk-auth | IAM service |
| sdk-data | @platform/sdk-data | Data Layer |
| sdk-events | @platform/sdk-events | Event Bus |
| sdk-notify | @platform/sdk-notify | Notify service |
| sdk-files | @platform/sdk-files | File Storage |
| sdk-money | @platform/sdk-money | Money service |
| sdk-audit | @platform/sdk-audit | Audit Log |
| sdk-flags | @platform/sdk-flags | Feature Flags (GoFeatureFlag) |
| sdk-ui | @platform/sdk-ui | Design System (no backend) |
| sdk-testing | @platform/sdk-testing | Test utilities (no backend) |
| sdk-codegen | @platform/sdk-codegen | Code generation (no backend) |
10 frontend packages
| Package | Purpose |
|---|---|
ui-shell | Vite 8 + React 19 + Module Federation 2.0 host application |
create-module | npx @platform/create-module — module scaffolding CLI |
embed-loader | Web Component loader for embeddable module widgets |
event-bridge | Browser CustomEvents bridge for cross-MFE communication |
build-config | Shared Vite, tsconfig, and ESLint configurations |
dev-server | Local development server with hot reload |
api-docs | API documentation generation utilities |
test-module | Reference module implementation for testing |
tsconfig | Shared TypeScript base configuration package |
sdk-integrations | @platform/sdk-integrations — Integration Hub client |
Architecture: sdk-core → Domain SDKs
All domain packages depend on @platform/sdk-core. sdk-core provides:
- Typed HTTP client — wraps
fetchwith retry logic, automaticAuthorization: Bearerheader injection, and RFC 9457 error parsing. PlatformErrorclass — typed error withtype,status,detail,traceId, anderrors[]fields.- Shared TypeScript types —
TenantId,UserId,CursorMeta,ListResponse<T>, and all primitive domain types. kernelfactory — the entry point that returns namespaced primitive interfaces afterkernel.init().
@platform/sdk-core
├── @platform/sdk-auth
├── @platform/sdk-data
├── @platform/sdk-events
├── @platform/sdk-notify
├── @platform/sdk-files
├── @platform/sdk-money
├── @platform/sdk-audit
└── @platform/sdk-flagsThe kernel.primitive() Pattern
Every SDK interaction goes through the kernel singleton. The pattern is
intentional: it prevents modules from constructing raw fetch calls,
ensures all requests carry the correct tenant context, and allows the
platform to swap the underlying transport without breaking modules.
// module entry point — initialize once
import { kernel } from '@platform/sdk-core';
await kernel.init({
tenantId: import.meta.env.VITE_TENANT_ID,
// token is managed automatically via the shell's auth context
});
// access any primitive via the same gateway
const contact = await kernel.data().retrieve('contacts', 'rec_01j9...');
const wallet = await kernel.money().retrieve('01j9p3...');
await kernel.audit().record('crm.contact.viewed', 'contact', contact.id);Each kernel.{primitive}() call returns an interface scoped to the
current tenant. Cross-tenant calls are structurally impossible — the
tenant ID is stored in sdk-core's internal context and injected into
every request header automatically.
Frontend Hooks vs Imperative API
The SDK provides two surfaces for every primitive:
Hooks — reactive, for React components
Hooks use TanStack Query v5 under the hood. They handle loading states, background refetching, and cache invalidation automatically.
import { useQuery, useMutation } from '@platform/sdk-data';
export function ContactList() {
const { data, isLoading } = useQuery('contacts');
const create = useMutation('contacts', {
onSuccess: () => {
// TanStack Query invalidates the 'contacts' cache automatically
},
});
if (isLoading) return <Spinner />;
return (
<>
{data.data.map((c) => <ContactRow key={c.id} contact={c} />)}
<button onClick={() => create.mutate({ name: 'New contact' })}>
Add
</button>
</>
);
}Imperative API — for business logic
Useful in event handlers, background jobs, lifecycle hooks, and anywhere outside a React component tree.
import { kernel } from '@platform/sdk-core';
// In an event handler registered at module startup
kernel.events().subscribe('crm.contact.created', async (event) => {
const contact = await kernel.data().retrieve('contacts', event.data.contactId);
await kernel.notify().send({
userId: event.data.assignedTo,
channel: 'in_app',
title: 'New contact assigned',
body: contact.name,
});
await kernel.audit().record(
'crm.contact.auto_notified',
'contact',
contact.id,
{ after: { notified: true } },
);
});SDK Package Reference
@platform/sdk-auth
Install: pnpm add @platform/sdk-auth
| Export | Type | Description |
|---|---|---|
useAuth() | hook | Returns { user, isLoading, login, logout, refresh } |
useRBAC() | hook | Returns { hasPermission(key), roles } |
requireRole(permission) | HOC | Wraps a component; redirects to /403 if permission absent |
verifyEmail(token) | async fn | Verifies email confirmation token |
resetPassword(token, newPassword) | async fn | Completes password reset flow |
inviteUser(email, roleId) | async fn | Sends 72-hour invite link |
registerProvider(config) | async fn | Registers OAuth/SSO provider for tenant |
listProviders() | async fn | Lists configured auth providers |
@platform/sdk-data
Install: pnpm add @platform/sdk-data
| Export | Type | Description |
|---|---|---|
useQuery(model, params?) | hook | Reactive list with cursor pagination |
useRecord(model, id) | hook | Reactive single record |
useMutation(model, options?) | hook | Create / update / delete with optimistic updates |
useRealtime(model, handler) | hook | CDC-backed real-time subscription |
kernel.data().create(model, data) | async fn | Create record |
kernel.data().retrieve(model, id) | async fn | Retrieve record by ID |
kernel.data().list(model, params?) | async fn | List with filters and cursor |
kernel.data().update(model, id, patch) | async fn | Partial update |
kernel.data().delete(model, id) | async fn | Soft delete |
@platform/sdk-events
Install: pnpm add @platform/sdk-events
| Export | Type | Description |
|---|---|---|
kernel.events().publish(type, data) | async fn | Publish domain event (manifest-declared only) |
kernel.events().subscribe(type, handler) | fn | Subscribe to event; returns Unsubscribe callback |
kernel.events().onEvent(type) | observable | RxJS observable stream of typed events |
Events are automatically scoped to the current tenant. Attempting to
publish a type not in manifest.events.publishes[] returns
403 Forbidden.
@platform/sdk-notify
Install: pnpm add @platform/sdk-notify
| Export | Type | Description |
|---|---|---|
kernel.notify().send(params) | async fn | Send single notification (202 Accepted) |
kernel.notify().sendBatch(notifications) | async fn | Send up to 500; returns jobId if async |
kernel.notify().sendFromTemplate(templateId, vars) | async fn | Template-based multi-channel send |
kernel.notify().registerChannel(config) | async fn | Register custom notification channel |
kernel.notify().listChannels() | async fn | List available channels for tenant |
@platform/sdk-files
Install: pnpm add @platform/sdk-files
| Export | Type | Description |
|---|---|---|
kernel.files().upload(file, options?) | async fn | Multipart upload with staging pipeline |
kernel.files().download(id) | async fn | Direct download blob |
kernel.files().getUrl(id, options?) | async fn | Presigned download URL |
kernel.files().uploadImage(file, cropData?) | async fn | Upload with server-side libvips processing |
kernel.files().processImage(id, params) | async fn | Post-upload image cropping and resizing |
kernel.files().getThumbnail(id, preset) | async fn | Fetch thumbnail URL by preset |
@platform/sdk-money
Install: pnpm add @platform/sdk-money
All money amounts are integers in the minor currency unit (cents for USD). Floating-point values are rejected at the schema level.
| Export | Type | Description |
|---|---|---|
kernel.money().credit(walletId, amount, currency, opts) | async fn | Credit wallet (requires Idempotency-Key) |
kernel.money().debit(walletId, amount, currency, opts) | async fn | Debit wallet (requires Idempotency-Key) |
kernel.money().transfer(fromId, toId, amount, currency, opts) | async fn | Atomic transfer between wallets |
kernel.money().getBalance(walletId) | async fn | Returns { available, pending, frozen } |
kernel.money().listTransactions(walletId, params?) | async fn | Paginated transaction history |
@platform/sdk-audit
Install: pnpm add @platform/sdk-audit
| Export | Type | Description |
|---|---|---|
kernel.audit().record(action, entityType, entityId, diff?) | async fn | Non-blocking audit write (Kafka-backed) |
kernel.audit().getHistory(entityType, entityId, params?) | async fn | Entity history from ClickHouse |
record() is fire-and-forget. The SDK returns immediately; the record
is written asynchronously via Kafka → ClickHouse.
@platform/sdk-flags
Install: pnpm add @platform/sdk-flags
| Export | Type | Description |
|---|---|---|
kernel.flags().isEnabled(flagKey) | fn | Boolean — evaluates in-memory (nanoseconds) |
kernel.flags().getVariant(flagKey) | fn | Returns variant string or default |
kernel.flags().snapshot() | fn | Full flag state for the current tenant session |
Flags are evaluated from an in-memory snapshot refreshed every 15
seconds. No network call per evaluation. If GoFeatureFlag is unavailable,
the SDK uses the stale in-memory cache; if the cache is empty, all flags
return false (fail-closed).
sdk-testing — Test Utilities
Install: pnpm add -D @platform/sdk-testing
Provides factory functions and mock implementations for every SDK package, so module tests run without a real backend:
import { kernel } from '@platform/sdk-testing';
// kernel from sdk-testing is a full mock — no HTTP calls
const mockContact = kernel.data().mockFactory('contacts', {
name: 'Test Contact',
email: '[email protected]',
});
// All SDK methods are jest-compatible spies
kernel.money().credit.mockResolvedValue({ id: 'tx_01j9...' });Built-in fixtures:
import { fixtures } from '@platform/sdk-testing';
const tenant = fixtures.tenant(); // TenantRow with valid UUID v7
const user = fixtures.user(); // UserRow with email and roles
const wallet = fixtures.wallet(); // WalletRow (USD, zero balance)
const file = fixtures.file(); // FileRow (status: available)sdk-codegen — Type Generation Pipeline
Install: pnpm add -D @platform/sdk-codegen
Generates TypeScript types and client code from platform specs:
# Generate types from module's OpenAPI spec
npx sdk-codegen openapi --input ./api/openapi.yaml --output ./src/gen/api.ts
# Generate event types from Avro schemas in Event Bus
npx sdk-codegen events --input services/event-bus/schemas/ --output ./src/gen/events.tsThe output files are type-safe, tree-shakeable, and imported directly
into the module's TypeScript codebase. The codegen step runs in CI on
every PR that modifies openapi.yaml or event schemas.
Installation Summary
# Install the primitives your module uses
pnpm add @platform/sdk-core @platform/sdk-auth @platform/sdk-data
# Add dev utilities
pnpm add -D @platform/sdk-testing @platform/sdk-codegen
# Or scaffold a new module with everything pre-configured
npx @platform/create-module my-moduleAfter scaffolding, kernel.init() is called once at module bootstrap.
All SDK packages read tenant context from the same shared Zustand store
injected by the UI Shell at login.
Security Model
Seven-layer encryption, CSP sandbox isolation, module signing, JWT ES256 signing key lifecycle, Argon2id password hashing, mTLS service mesh, and the key hierarchy from HSM to application DEK.
Versioning & Deprecation Policy
Platform-Kernel API versioning strategy, Semver rules for modules and SDK packages, deprecation lifecycle with Deprecation/Sunset headers, Protobuf backward-compatibility enforcement, and the Primitive Freeze checklist.