@arki/dot

archive

for @arki/[email protected] · Tier 1: the stack

TypeScript-first application composition framework for the ARKI package family.

Version 0.1.0 archive

Install

bun add @arki/[email protected]
Other package managers
npm install @arki/[email protected]
pnpm add @arki/[email protected]
yarn add @arki/[email protected]

Release health for @arki/[email protected]

Doc examples

No example report was recorded for this version.

Open gaps

2 release checks fail · 6 gaps were found by demos

  • checkfile-agents-md
  • checkpkg-engines
  • Plugin hook spans are created with tracer.startSpan (not startActiveSpan), so work done i… found by drop-studio
  • Plugins do not declare their source package found by drop-studio
  • Instrumentation scope version is hard-coded (0.1.0) while the package is 0.4.1, and plugi… found by drop-studio
  • Running app.boot() under context.with(ROOTCONTEXT.setValue(key, v), …) stalls boot under… found by drop-studio
  • The dot bin runs under Node (#!/usr/bin/env node) and cannot import a .ts app file there… found by drop-studio
  • createAppRuntime(app).get() returns a DotApp type that is not assignable to the result ty… found by faultline

Used by demos

No demo uses this package yet.

Runtimes

No engines field, so no runtime requirement is declared.

Benchmarks

No benchmarks are published for this version.

README

TypeScript-first application composition framework for the ARKI package family.

@arki/dot is the kernel that wires pips, lifecycle hooks, dependency graphs, and diagnostics into a deterministic application boot. It gives library authors a stable contract for declaring how their package participates in an app, and gives app developers a single place to wire those packages together.

What is a pip?

A pip is the unit a DOT app is built from — one self-describing, lifecycle-aware piece of an application. Each pip:

  • declares a name, version, and the kinds of services it provides;
  • runs a 5-hook lifecycle — configure → boot → start → stop → dispose;
  • publishes typed services to a shared, type-safe registry that later pips can read from;
  • composes deterministically — pips boot in declaration order and dispose in reverse.

The name comes from the small dots on dice, dominoes, and music notation: each pip is one small mark, and the combination of pips is what gives the app its value. Two pips on a die make a value of two; six pips make six. The pips are the app — not optional add-ons to a hidden core.

import { defineApp } from '@arki/dot';
import { env } from '@arki/env/dot';      // env pip
import { db } from '@arki/db/dot';        // db pip
import { kv } from '@arki/kv/dot';        // kv pip
 
const app = await defineApp('orders')
  .use(env({ schema: { /* ... */ } }))     // 1st pip — provides services.env
  .use(db({ relations }))                  // 2nd pip — provides services.db
  .use(kv({ url: process.env.KV_URL! }))   // 3rd pip — provides services.kv
  .boot();                                  // pips boot in declaration order
 
await app.services.db.query(/* ... */);
await app.dispose();                       // pips dispose in REVERSE order

Each /dot subpath exports a pip for that package. The subpath names the framework the adapter targets (DOT), not the unit (which is a pip).

Why not "plugin"? Plugins suggest optional add-ons to a core. DOT's reality is the opposite: there is no hidden core — the pips are the app. "Pip" names that truth, and ties the framework to the DOT name etymologically (a dot, a pip, a small mark that gains meaning by combining with others).

Installation

npm install @arki/dot
# or
bun add @arki/dot

Quick start

import { defineApp, defineDotPip } from '@arki/dot';
 
const billingPip = defineDotPip({
  name: 'billing',
  version: '1.0.0',
  configure(ctx) {
    // Validate config, register schemas, no I/O.
  },
  async boot(ctx) {
    // Open connections, register providers.
    return { stripe: makeStripeClient() };
  },
  async start(ctx) {
    // Begin processing — workers, subscriptions, schedulers.
  },
  async stop() {
    // Stop processing — drain workers, unsubscribe.
  },
  async dispose() {
    // Close connections, free resources.
  },
});
 
const app = defineApp('acme')
  .use(billingPip);
 
await app.configure();
await app.boot();
await app.start();
 
// app.manifest, app.diagnostics — agent-friendly envelopes.
 
await app.stop();
await app.dispose();

Pip authoring

defineDotPip(config) accepts five lifecycle hooks. Each receives a phase-specific context with services contributed by earlier pips:

HookPurpose
configureValidate static config; declare schemas, routes, services. No I/O.
bootOpen connections; register providers; return a typed kit.
startBegin processing (workers, subscribers, schedulers).
stopStop processing in reverse dependency order.
disposeFree resources after stop.

Pips can declare dependencies on others by name. The kernel computes a topological order and runs hooks deterministically — same input, same order, every time.

Lifecycle

defineApp(name) returns a builder. Calling .use(pip) accumulates pips. The lifecycle then flows:

defined ──configure()──▶ configured ──boot()──▶ booted ──start()──▶ started
                                                                       │
                       disposed ◀──dispose()── stopped ◀──stop()───────┘

boot() runs configure() implicitly if you skipped it. start() runs boot() implicitly. stop() and dispose() always run in reverse dependency order, even when an earlier hook failed — failure isolation is part of the contract.

CLI

@arki/dot ships a small CLI for scaffolding and inspecting apps:

# Scaffold a minimal DOT app (package.json, tsconfig, app entrypoint,
# env schema, AGENTS.md, README, gitignore, vitest boot test).
dot new my-app
 
# Preview the file operations without writing anything.
dot new my-app --dry-run --json | jq '.operations'
 
# Print the app manifest as a structured envelope.
dot explain --app ./my-app.ts
 
# Run boot-time diagnostics; non-zero exit if any check fails.
dot doctor --app ./my-app.ts
 
# Every command supports --json for agent-friendly output.
dot explain --app ./my-app.ts --json | jq '.data.pips'

The CLI emits the same envelope shape as the in-process diagnostics snapshot (app.diagnostics), so the same downstream tools can consume either.

dot new <app-name>

Scaffolds a minimal DOT app under <app-name>/ (override with --target). The scaffold ships a defineApp(...) entrypoint wired to @arki/env/dot, a vitest that boots the app and asserts the manifest shape, and an AGENTS.md documenting verification commands and the public/private boundary for agents working in the generated tree. Pass --dry-run --json to inspect the exact file operations (path, action, contentHash, contentBytes, reason) before committing them to disk; --force overwrites pre-existing files in the target directory.

Templates live at templates/app-minimal/ and ship with the published tarball.

Architecture

@arki/dot is intentionally small: it defines the contracts (pip shape, lifecycle hooks, manifest schema, diagnostics envelope) and runs them. Adapters that bridge databases, queues, auth providers, and HTTP routers live in their own packages and consume @arki/dot as a peer dependency.

This keeps the kernel free of optional dependencies and lets each adapter ship at its own cadence.

Documentation

The full docs live in docs/:

  • Principles — read first. The five rules every API, error, and PR is measured against. Slightly playful, very precise.
  • Quickstart — boot your first app in five minutes.
  • Pip authoring — write your own DotPip.
  • Lifecycle — the 5-hook contract.
  • Diagnostics — app.manifest, app.diagnostics, dot explain, dot doctor.
  • Adapter authoring — expose your package as a DOT pip.
  • Agent guide — how coding agents inspect, modify, and verify DOT apps.
  • Release policy — SemVer and deprecation.

Agent-discoverable index: llms.txt.

License

MIT — see LICENSE.

Public imports

This version has no API index.