API Reference
Complete API reference for createTeardown, the Teardown object, the domain services, and the cross-cutting ports.
createTeardown
Wire a storage implementation and configuration into the Teardown runtime.
function createTeardown(options: TeardownOptions): Teardownimport { createTeardown } from "@teardown/server";
const td = createTeardown({
storage: createMemoryStorage(),
config: { sessionSecret: process.env.SESSION_SECRET! },
});Throws if config.sessionSecret is absent and no custom signer is supplied.
TeardownOptions
| Property | Type | Required | Description |
|---|---|---|---|
storage | TeardownStorage | Yes | The pluggable storage layer |
config | TeardownConfig | No | Runtime configuration |
tenant | TeardownTenant | No | { orgId, projectId } → single-tenant: skips API-key auth and makes td-api-key/td-org-id/td-project-id unnecessary (hosted-only). Omit for multi-tenant |
cache | CachePort | No | Cache for version/session lookups. Default: no-op |
logger | LoggerPort | No | Structured logger. Default: no-op |
metrics | MetricsPort | No | Metrics sink. Default: no-op |
clock | ClockPort | No | Clock + uuid source. Default: system |
signer | TokenSigner | No | Session-token signer. Default: HS256 (jose) using config.sessionSecret |
onVersionStatusChange | OnVersionStatusChange | No | Hook fired on a real version-status change |
onBuildStatusChange | OnBuildStatusChange | No | Hook fired on a real build-status change |
Teardown
The object returned by createTeardown.
| Property | Type | Description |
|---|---|---|
services | TeardownServices | The wired domain services |
router(info?) | (info?: RuntimeInfo) => TdRouter | Build the ingest HTTP router |
storage | TeardownStorage | The storage you passed in |
config | ResolvedConfig | The configuration with defaults applied |
cache | CachePort | The resolved cache port |
logger | LoggerPort | The resolved logger port |
metrics | MetricsPort | The resolved metrics port |
clock | ClockPort | The resolved clock port |
signer | TokenSigner | The resolved token signer |
SDK_NAME
import { SDK_NAME } from "@teardown/server";
// "@teardown/server" — a stable namespace for cache keys, headers, etc.Services
td.services exposes eight domain services. For a self-hosted ingest server, the only ones the mounted router uses are identify and events (with forceUpdate/session behind them) — the rest are for version management and custom integrations. Unless noted, methods return an AsyncResult<T> ({ success: true; data: T } | { success: false; error: string }); the version/build services return a typed error union instead.
| Service | Purpose |
|---|---|
identify | Identify a device/user → session + version_info |
events | Ingest a batch of events |
forceUpdate | Resolve + provision version/build status (the ingest read path) |
session | Create / reuse / look up device sessions |
environment | Resolve, create, and delete environments |
pushTokens | Upsert / invalidate / read a device's push token |
versions | Version management (CRUD + status cascade) |
builds | Build management (CRUD) |
identify
identify(headers: IdentifyHeaders, request: IdentifyRequest): Promise<
| { success: true; data: IdentifyData }
| { success: false; error: string }
>IdentifyData is { session_id, device_id, user_id, token, version_info }. This is the full payload the POST /v1/identify route returns.
events
processEvents(headers: EventsHeaders, request: EventsRequest): Promise<
| { success: true; data: ProcessEventsResult }
| { success: false; error: string }
>ProcessEventsResult is { eventIds: string[]; processedCount: number; failedCount: number }. The handler maps it to the snake_case wire payload (event_ids / processed_count / failed_count). A batch larger than config.maxEventsPerBatch is rejected. A user_signed_out event invalidates the device's push token.
forceUpdate
| Method | Returns | Description |
|---|---|---|
getOrCreateVersionBuild(projectId, versionName, buildNumber, platform) | AsyncResult<{ versionId; buildId }> | Race-safe get-or-create of the version + build; caches the resolved ids for 15 minutes |
checkVersionStatus(input) | AsyncResult<VersionInfo> | Resolve the client version_info (most-restrictive of version vs build status) |
input is { projectId, versionName, buildNumber, platform } where platform is "IOS" | "ANDROID".
session
| Method | Returns | Description |
|---|---|---|
getOrCreateSession(deviceId, deviceInfo, context, versionBuildInfo, sdkInfo?) | AsyncResult<{ session; token; isExisting }> | Reuse a valid session (extending its expiry) or create a new one with a signed JWT |
createSession(deviceId, deviceInfo, claims, versionBuildInfo, sdkInfo?) | AsyncResult<{ session; token }> | Create a new session |
extendSessionExpiry(sessionId, deviceId) | AsyncResult<void> | Extend a session by config.sessionReuseExtendMs |
getSession(sessionId) | AsyncResult<SessionEntity | null> | Look up a session by id |
getSessionByToken(token) | AsyncResult<SessionEntity | null> | Look up a session by token (cache-first) |
getValidSessionForDevice(deviceId) | AsyncResult<{ session; token } | null> | The current valid session for a device (verifies the token) |
deviceId here is the internal device id (DeviceEntity.id).
environment
| Method | Returns | Description |
|---|---|---|
getEnvironment(projectId, slug) | AsyncResult<EnvironmentEntity | null> | Find an environment by project + slug |
createEnvironment(projectId, name, slug, type) | AsyncResult<EnvironmentEntity> | Create an environment (slug unique per project) |
deleteEnvironment(environmentId) | AsyncResult<void> | Delete an environment (enforces the last-env / last-PRODUCTION / last-DEVELOPMENT rules) |
pushTokens
| Method | Returns | Description |
|---|---|---|
upsertPushToken(orgId, projectId, deviceId, notificationsInfo) | AsyncResult<{ token; isNew }> | Upsert the device's push token (skips unless enabled + granted + token present) |
invalidateToken(deviceId) | AsyncResult<void> | Mark the device's push token(s) invalid |
getTokenByDeviceId(deviceId) | AsyncResult<PushTokenEntity | null> | Read the device's push token |
versions
| Method | Returns | Description |
|---|---|---|
getVersionById(projectId, versionId) | AsyncResult<VersionEntity, VersionServiceError> | Get one version |
getVersionsByIds(projectId, versionIds) | AsyncResult<VersionEntity[], VersionServiceError> | Get many (≤ 100) |
searchVersionsByProject(params) | AsyncResult<VersionsPage, VersionServiceError> | Paginated/sortable search |
updateVersion(projectId, versionId, data, options?) | AsyncResult<VersionEntity, VersionServiceError> | Update status and/or notes (status cascades to builds) |
data is { status?: ProjectVersionStatus; notes?: string | null }; options is { sendNotification?: boolean }. See Version Management.
builds
| Method | Returns | Description |
|---|---|---|
getBuildById(projectId, buildId) | AsyncResult<BuildEntity, BuildServiceError> | Get one build |
getBuildsByIds(projectId, buildIds) | AsyncResult<BuildEntity[], BuildServiceError> | Get many (≤ 100) |
searchBuildsByProject(params) | AsyncResult<BuildsPage, BuildServiceError> | Paginated/sortable search |
updateBuild(projectId, buildId, data, options?) | AsyncResult<BuildEntity, BuildServiceError> | Update status and/or notes (marks the build overridden) |
TeardownConfig
interface TeardownConfig {
sessionSecret?: string; // HMAC key signing client session tokens; required unless a custom signer is supplied
sessionTokenExpiry?: string; // jose-format, default "15m"
sessionTtlMs?: number; // session row TTL, default 900000 (15m)
sessionReuseExtendMs?: number;// reuse extension, default 300000 (5m)
maxEventsPerBatch?: number; // default 100
}resolveConfig(config?) applies the defaults and is exported alongside the ResolvedConfig type if you need the resolved values directly.
Cross-cutting ports
All cross-cutting ports are optional, with a no-op or system default. Import their types from @teardown/server/ports.
CachePort
Matches @teardown/redis's CacheInterface, so a Redis cache is structurally assignable. Default: a no-op NullCache.
interface CachePort {
get<T>(key: string): Promise<T | null>;
set(key: string, value: unknown, ttlSeconds?: number): Promise<boolean>;
del(key: string): Promise<boolean>;
}LoggerPort
Default: noopLogger.
interface LoggerPort {
debug(message: string, context?: Record<string, unknown>): void;
info(message: string, context?: Record<string, unknown>): void;
warn(message: string, context?: Record<string, unknown>): void;
error(message: string, context?: Record<string, unknown>): void;
}MetricsPort
Generic counter/histogram sink. Default: noopMetrics. Attributes are { orgId?, projectId?, environment?, status? }.
interface MetricsPort {
counter(name: string, value: number, attributes?: MetricAttributes): void;
histogram(name: string, value: number, attributes?: MetricAttributes): void;
}ClockPort
Injectable time + uuid source (edge-safe and test-deterministic). Default: systemClock.
interface ClockPort {
now(): Date;
uuid(): string;
}TokenSigner
Session-JWT signer. Default: an HS256 jose signer built from config.sessionSecret.
interface TokenSigner {
sign(claims: SessionTokenClaims): Promise<string>;
verify(token: string): Promise<SessionTokenPayload | null>;
}
interface SessionTokenClaims {
sessionId: string;
deviceId: string;
userId: string;
environmentId: string;
projectId: string;
orgId: string;
}createJoseSigner({ secret, expiry? }) builds the default signer and is exported if you want to construct it explicitly (or run it on a custom secret/expiry):
import { createJoseSigner } from "@teardown/server";
const signer = createJoseSigner({ secret: process.env.SESSION_SECRET!, expiry: "30m" });
const td = createTeardown({ storage, signer });Subpath exports
| Import | Contents |
|---|---|
@teardown/server | createTeardown, TeardownOptions, Teardown, TeardownTenant, SDK_NAME, createJoseSigner, config + hook types |
@teardown/server/ports | TeardownStorage, the *Repository interfaces, the *Entity/New*/*Patch types, StorageError, and the cross-cutting ports |
@teardown/server/contracts | The wire-contract DTO types + validateIdentifyRequest / validateEventsRequest |
@teardown/server/http | The router builder, normalized Td* primitives, the authenticator, CORS constants |
@teardown/server/http/fetch | toFetchHandler (Web-standard adapter) |
@teardown/server/http/elysia | teardownElysia (Elysia plugin) |
@teardown/server/adapters/memory | createMemoryStorage, MemorySeed |