A TypeScript-first application composition framework.
Tiny kernel. Package-owned adapters. First-class diagnostics.
bun add @arki/dot@arki/dot is the kernel — the framework that ties every
other @arki/* package together. A DOT app is composed of pips:
small, lifecycle-aware units that publish typed services and compose
deterministically. The kernel itself is tiny; everything else lives in
a package you opt into with .use().
import { defineApp } from '@arki/dot';
import { env } from '@arki/env/dot';
import { db } from '@arki/db/dot';
import { kv } from '@arki/kv/dot';
export const app = await defineApp('billing')
.use(env({ schema }))
.use(db({ schema: dbSchema }))
.use(kv())
.boot();
// Each .use() extends the inferred app type. After .boot() the services
// are typed end-to-end — no DI container, no decorators, no manual wiring.
app.services.env.DATABASE_URL; // string (inferred from your env schema)
app.services.db.query.users; // typed query builder
await app.services.kv.get('key'); // typed get/setEvery pip plugs into the same five hooks, run in declaration order (teardown in reverse):
configure ─► boot ─► start ─► stop ─► dispose
configure— read env, validate, register schemas. No I/O.boot— open connections, run migrations, hydrate caches.start— begin accepting work (HTTP listen, queue subscribe).stop— drain in-flight work; refuse new requests.dispose— release resources; final flush.
Failure at any hook produces a DotDiagnostic with a stable code, a
severity, and a remediation URL. The kernel collects them in the
DotAppManifest so a CLI, a probe, or an agent can read the same shape.
A pip declares the services it needs as typed witnesses and
provides services by returning them from boot — provides are
inferred from the return type, never declared separately:
import { pip, service } from '@arki/dot';
const billing = pip({
name: 'billing',
needs: { db: service<Db>() }, // typed injection witness
async boot({ db }) {
return { billing: makeBilling(db) }; // the return IS the provides
},
});The kernel infers app.services.<key> from the chain of .use() calls,
and the builder guard makes wrong wiring a compile error at the .use()
call site — unsatisfied needs, type mismatches, and key collisions do
not compile. Declaration order is boot order, so dependency cycles are
unrepresentable. Adding a pip extends the types, and removing one is a
compile-time break, not a runtime surprise. No decorators. No
reflection. No global registry.
Cross-package contracts use tokens (token<T>()('arki.db')) with
free-choice local aliases; rename() mounts a second instance of an
adapter without key collisions; lazy() defers an expensive open until
first use, with kernel-managed cleanup in reverse order.
- Every error has a stable code and a remediation URL.
- Every CLI command emits a structured JSON envelope under
--json. dot doctorreturns a machine-readable snapshot of the app graph.dot explainreturns the static manifest — what the app would boot.
Read packages/dot/README.md for the full
DOT reference: defineApp, the pip contract, lifecycle hooks, manifest
and diagnostics shape, and the CLI.
Every TypeScript backend eventually composes:
env + db + kv + queue + auth + RPC + jobs + diagnostics + tests
You can wire this by hand. Most teams do. But the glue is the bug surface, and the glue rots faster than the libraries it connects.
ARKI is that glue, factored into composable packages, with a kernel that owns the lifecycle and a release pipeline that proves every published package passes the same gates — pack audit, leak scan, fixture install, typecheck, docs coverage. Failed gate, no publish.
bunx @arki/dot new my-app
cd my-app
bun install
bun run devEvery package below is published independently to npm under the
@arki/* scope. Each one has its own README with examples and
contract notes.
| Wave | Packages |
|---|---|
| Kernel | @arki/dot — application composition kernel |
| Foundation | @arki/assert, @arki/contracts, @arki/log, @arki/env, @arki/ts, @arki/resilience, @arki/date, @arki/string, @arki/slugify, @arki/clock |
| Adapters | @arki/db, @arki/kv, @arki/event-sourcing |
Five rules we don't break to ship faster:
- No shortcuts. Every published package passes the release scorecard (pack audit, leak scan, fixture install, typecheck, docs coverage) before it is published. Failed gate, no publish.
- Test the boundary, not the implementation. Packed tarballs, public exports, CLI output, JSON envelopes, generated apps — those are what is tested. Private helpers are not tested directly.
- Framework quality bar: Laravel / Rails-level discipline. Coherent conventions. Excellent generators. Documented errors. Tiny polished kernel over broad rough framework.
- Peer respect, not ridicule. AdonisJS, ElysiaJS, Hono, NestJS are excellent at the jobs they were built for. DOT does a different job. We learn from them.
- Agents are first-class readers. Every error has a stable code.
Every CLI has a
--jsonenvelope. Every page has anllms.txtentry. The site is tested for agent comprehension, not only human comprehension.
ARKI is on the experimental track (0.0.x for foundation packages,
0.1.x for the kernel and adapters). The kernel is stable in shape;
adapters are stable per their per-package README; the public API may
evolve before 1.0. The release scorecard at the upstream workspace
gates every publish.
MIT. See LICENSE.
This repository is the generated public mirror of the ARKI workspace. Sources are not edited here directly — open issues and discussion threads instead, and changes land upstream and re-export.