Atlas: Architecture Overview#

Atlas is a collection of composable, functional Bun/TypeScript packages for building APIs, full-stack applications, and CLI tools. This document describes the architecture, package relationships, and design philosophy.

Distribution and dependency graph#

Public imports use @wess/atlas/<module>. The facade forwards each entry point to one implementation library, such as @wess/atlas-server; it has no mandatory runtime dependencies or aggregate root entry point. A selected-entry dependency catalog includes runtime imports, type imports, reexports, and literal dynamic imports so isolated consumer builds have the packages needed for typechecking too.

Source modules remain focused folders under packages/. Distribution rewrites cross-module relative imports to implementation package imports. Implementation peers are optional because different entry points use different subsets. The CLI installs each selected entry point's complete closure, rather than every peer.

Entry pointBoundary
auth/password, auth/token, auth/sessionNo database flows, server, or social providers
auth/flowsDatabase queries and server pipes
auth/socialProvider flows and server pipes; private cookie keeps PKCE verifier
db/queryQuery/schema API without either database driver
db/postgres, db/sqliteOnly the chosen Bun driver
db/changesetStructural safeParse validation; no mandatory Zod dependency
adminServer routes and validation; no browser UI imports
admin/ui, ui/<block>Browser components and selected frontend peers
cli, cli/command, cli/foremanRuntime CLI APIs without scaffolding or docs
cli/scaffold, cli/docsExplicit development tooling

Aggregate module entry points remain available when their whole feature set is wanted. Measure installed bytes, browser bundles, startup and sustained memory separately; a downloaded file is not necessarily loaded at runtime.

Core Packages#

Config#

@wess/atlas/config is the foundation. It provides typed environment variable loading that reads .env at startup and produces a frozen, immutable config object.

const config = defineConfig({
  database: env("DATABASE_URL"),
  port: env("PORT", { parse: Number, default: "3000" }),
})

All other packages optionally depend on config or accept config values directly. No forced wiring.

Database#

@wess/atlas/db is the largest package, with three layers:

  1. Query Builder — Ecto-inspired fluent chains for SELECT, INSERT, UPDATE, DELETE, JOIN operations. Fully immutable, functional API.
  2. Drivers — Uniform interface to Postgres (via Bun.sql) and SQLite (via bun:sqlite) with transaction support.
  3. Schemas & Changesets — Type-safe table definitions and safeParse-compatible input validation.
const users = defineSchema("users", {
  id: column.serial().primaryKey(),
  email: column.text().unique(),
})

const query = from(users).where(q => q("email").equals("[email protected]"))
const result = await db.one(query)

Server#

@wess/atlas/server wraps Bun.serve with a Plug-inspired pipe system. Requests flow through immutable Conn objects that pipes transform.

Sub-modules:

  • @wess/atlas/server — Core HTTP routing with get(), post(), put(), patch(), del() route builders, plus the adapter pattern (createAdapter, compose) for running multiple listeners
  • @wess/atlas/server/ws — WebSocket support
  • @wess/atlas/server/sse — Server-Sent Events
const logger = pipe(c => { console.log(c.method); return c })
const auth = pipe(c => {
  const token = c.headers.get("authorization")
  return token ? assign(c, { userId: decode(token).id }) : halt(c, 401)
})

serve({
  routes: [
    get("/users", pipeline(logger, auth)(handler)),
  ],
})

Pipes are composable via pipeline(), which short-circuits on halt(). This approach is borrowed from Elixir and is more testable and readable than middleware.

Edge#

@wess/atlas/edge is a TLS-terminating reverse proxy with built-in Let's Encrypt automation — the layer that would otherwise be Caddy or nginx in front of a Bun app.

  • ACME v2 client — ECDSA P-256 account, JWS-signed requests, HTTP-01 challenge solver, hand-rolled CSR via a small DER encoder. No external deps.
  • Reverse proxy — forwards requests with X-Real-IP, X-Forwarded-For (appended), X-Forwarded-Proto, X-Forwarded-Host. Hop-by-hop headers stripped. Host rewritten unless preserveHost: true.
  • Cert lifecycle — certs persist to a configurable store (filesystem default, in-memory for tests). A renewal timer reissues 30 days before expiry and server.reload({ tls }) swaps without dropping connections.
  • Localhost dev — when every site host matches isLocalHost, the edge runs plain HTTP on a non-privileged port. No certs, no sudo. The same edge.ts runs in dev and prod.
import { defineEdge, proxy } from "@wess/atlas/edge"

defineEdge({
  acme: { email: "[email protected]", storage: "/var/atlas/edge" },
  sites: [{
    host: process.env.DOMAIN!,
    compress: ["gzip", "zstd"],
    routes: [{ handler: proxy("http://app:3000") }],
  }],
}).listen()

The templates/edge scaffold ships a complete deploy pattern: Procfile for local, Dockerfile + compose.yaml for production, with the cert volume and ACME staging-then-prod recipe pre-wired.

Auth#

@wess/atlas/auth provides:

  • Primitives — hash(), verify(), token.sign(), token.verify() built on Bun's crypto
  • Flows — Prebuilt pipes like signup(), login(), requireAuth(), passwordReset() that work with @wess/atlas/server and @wess/atlas/db
  • Social login (@wess/atlas/auth/social subpath) — pluggable OAuth-client for Google, GitHub, Apple, Microsoft, Facebook, X (Twitter), and TikTok. PKCE-S256 mandatory, state + verifier held in a signed HttpOnly cookie so the flow stays stateless. Your app owns the user table — onSuccess receives a normalized SocialProfile and you decide upsert/link.
const signupPipe = signup({
  db,
  table: "users",
  fields: ["email", "password"],
  onSuccess: (c, user) => json(c, 201, user),
})

// Social — same Conn/PipeFn shape:
const social = socialAuth({
  secret,
  providers: {
    google: google({ clientId, clientSecret, redirectUri }),
    github: github({ clientId, clientSecret, redirectUri }),
    tiktok: tiktok({ clientKey, clientSecret, redirectUri }),
    // …apple, microsoft, facebook, twitter
  },
})
get("/auth/google", social.start("google"))
get("/auth/google/callback", social.callback("google", {
  onSuccess: async (c, { profile }) => json(c, 200, { profile }),
}))

Security#

@wess/atlas/security ships hardening primitives most apps end up writing themselves:

  • withSecurityHeaders() — strict default headers (HSTS, COOP/CORP, Permissions-Policy) and a CSP that defaults to 'self'-only with no inline script in production. Also stashes the real Bun socket peer onto req.peerIp so downstream rate-limit / audit code does not have to trust client-supplied X-Forwarded-For.
  • createDbRateLimit() / createMemoryRateLimit() — atomic UPSERT on Postgres, transactional read-modify-write on SQLite, in-memory for tests.
  • clientIp(req, { trustedProxies }) — only honors forwarded headers when the request actually arrived from a configured trusted proxy.
  • createSessionStore() — DB-backed, revocable JWT sessions with last_used_at tracking and a sweep helper. Pair with @wess/atlas/auth#requireAuth.
  • generateSecret / verifyTotp / otpauthUrl / generateBackupCodes — pure node:crypto TOTP with backup codes.
  • decideInline() — safe-MIME allowlist for Content-Disposition: inline (never inlines SVG).
  • createAuditLogger() — fire-and-forget audit-event recorder; never throws, never blocks the response.

OAuth#

@wess/atlas/oauth is an OAuth 2.1 server: PKCE-required authorization code, refresh-token rotation, device-code flow, RFC 8414 discovery, RFC 7009 revoke, and admin client management.

import { oauthRoutes } from "@wess/atlas/oauth"
import { requireAuth } from "@wess/atlas/auth"

serve({
  routes: [
    ...oauthRoutes({
      db,
      secret,
      scopes: ["read", "write"],
      loadUser,
      buildAccessTokenClaims,
      requireUser: requireAuth({ secret }),
      requireAdmin,
    }),
    ...appRoutes,
  ],
})

oauthRoutes(cfg) returns every endpoint as a flat Route[] ready to spread into serve. Mount only what you need by calling the per-flow factories (oauthAuthorizeRoutes, oauthTokenRoutes, …) directly. Sweeps for expired auth codes / refresh tokens / device codes are exported separately so you can run them on a schedule.

Email#

@wess/atlas/email is a provider-agnostic transport plus a small HTML shell and two stock templates.

import { createEmailer, inviteEmail } from "@wess/atlas/email"

const emailer = createEmailer({ apiKey: process.env.RESEND_API_KEY, from: process.env.RESEND_FROM })
await emailer.send({ to: "[email protected]", ...inviteEmail({ inviterName, product, signupUrl }) })

createEmailer returns the real Resend transport when both env vars are set and a console transport otherwise — dev environments stay unblocked without configuring a sending domain. send never throws; failures come back as { ok: false, error }. The included layout() is a 560px Outlook-friendly card; always pass user-supplied strings through escapeHtml.

Share#

@wess/atlas/share is the smallest practical layer for "share this link" UX. Pure URL builders for the eight channels users actually use, plus a server-side shareEmail that dispatches through any @wess/atlas/email transport.

import { share, shareUrl, shareEmail } from "@wess/atlas/share"

shareUrl("twitter", { url, title, hashtags: ["atlas"] })
// → https://twitter.com/intent/tweet?url=…&text=…&hashtags=atlas

share({ url, title, text })
// → { twitter, facebook, linkedin, reddit, whatsapp, telegram, sms, email }

await shareEmail({
  emailer,
  to: "[email protected]",
  sharerName: "Wess",
  content: { url, title },
})

The URL builders have zero dependencies; shareEmail reuses the same layout() shell as inviteEmail/passwordResetEmail and escapes untrusted strings the same way.

Storage#

@wess/atlas/storage provides S3-compatible object storage with:

  • upload() — PUT file to bucket
  • download() — GET file from bucket
  • presign() — Generate presigned URLs for direct client access
  • list() — List objects by prefix

Uses AWS Signature V4 (implemented from scratch, ~250 lines) with no external dependencies.

Cache#

@wess/atlas/cache wraps Bun.redis with:

  • createCache() — Redis-backed cache
  • createMemoryCache() — In-memory (for testing)
  • cached() — Cache-aside pattern: fetch from cache, fallback to function, store result
  • invalidate() — Bust cache keys
const getUser = cached(cache, "user", async (id) => {
  return await db.one(from(users).where(q => q("id").equals(id)))
}, { ttl: 600 })

Request#

@wess/atlas/request is an HTTP client built on fetch with:

  • request() — One-off requests
  • createClient() — Preconfigured clients with base URL, headers, retries
  • Providers — Drop-in configs for GitHub, Stripe, OpenAI, Resend (import from @wess/atlas/request/providers)
  • Retry logic with exponential backoff
  • Request/response interceptors
import { github } from "@wess/atlas/request/providers"

const gh = github({ token: process.env.GITHUB_TOKEN! })
const repos = await (await gh.get("/user/repos")).json()

CLI#

@wess/atlas/cli provides:

  • Command parser — Define commands, flags, subcommands; parse argv
  • Foreman — Procfile parser and concurrent process runner with colored output
  • Built-in commands — atlas init (scaffold project), atlas add (add packages), atlas dev (start dev server), atlas mcp (launch MCP server)
cli("myapp", [
  command("serve", {
    flags: { port: flag("p", { type: "number", default: 3000 }) },
    run: ({ flags }) => startServer(flags.port),
  }),
])

await foreman({ web: "bun run server.ts", worker: "bun run worker.ts" })

Migrate#

@wess/atlas/migrate manages timestamped SQL migrations with up/down support.

migrate.create("./migrations", "add_users")
await migrate.up(db, "./migrations")
await migrate.status(db, "./migrations")

Tracks applied migrations in schema_migrations table. Works with Postgres and SQLite.

UI#

@wess/atlas/ui is a modular React package with its own components and independent blocks:

  • @wess/atlas/ui/styles.css — The compiled stylesheet; import it once
  • @wess/atlas/ui/components — Primitives (Button, TextInput, Table, Tabs, ...)
  • @wess/atlas/ui/headless — Behavior hooks with no markup or styles
  • @wess/atlas/ui/factory — component() and createField() for building new primitives
  • @wess/atlas/ui/provider — Color scheme, light/dark/system mode, and layout shell
  • @wess/atlas/ui/forms — Form primitives and helpers
  • @wess/atlas/ui/table — Data tables with sort/filter/pagination
  • @wess/atlas/ui/auth — Login, signup, password reset pages
  • @wess/atlas/ui/storage — File upload and image preview
  • @wess/atlas/ui/nav — Navigation components
  • @wess/atlas/ui/cache — Cache inspector and status badge
  • @wess/atlas/ui/ai — Chat panel, message list, streaming display

Each block is independently importable and tree-shakeable.

Admin#

@wess/atlas/admin auto-generates a Django-style admin panel from @wess/atlas/db schemas. Provides:

  • API routes — CRUD endpoints for each model
  • Admin SPA — React UI with list, detail, create views, search, filters, bulk actions, custom actions, query builder
  • Metadata routes — Schema introspection, field info, relation discovery
const panel = admin({
  db,
  models: [
    model({
      schema: users,
      searchFields: ["email", "name"],
      filterFields: ["status"],
      bulkActions: ["delete", "export"],
    }),
  ],
})

serve({ routes: panel.mount([]) })

Serves SPA at /admin with full CRUD UI.

MCP#

@wess/atlas/mcp provides a Model Context Protocol server for AI/LLM debugging and introspection. It exposes your app's database, routes, config, and logs to AI agents through the standard MCP protocol.

import { collectTools, createContext, createMcpServer } from "@wess/atlas/mcp"

const ctx = createContext({ db, routes: myRoutes, config })
const mcp = createMcpServer(collectTools(ctx), ctx)

mcp.start()

Launch via CLI: atlas mcp. AI agents can then query your database, inspect routes, view config, and read logs interactively.

AI#

@wess/atlas/ai provides a unified interface for AI/LLM operations with zero external dependencies — it calls provider REST APIs directly via fetch.

  • Providers — createProvider({ provider, key }) returns an AiProvider for "openai", "anthropic", or "ollama"
  • Chat — provider.chat(opts) for completions, provider.chatStream(opts) for streaming StreamChunks
  • Conversations — createConversation/send for immutable message-history tracking
  • Embeddings — embed(provider, inputs) plus an in-memory createVectorStore()
  • RAG — index(rag, id, text) + query(rag, question) over a { ai, store, topK? } bag
  • Agents — runAgent({ ai, system?, tools, maxIterations? }, prompt) for tool-using loops
  • Server pipe — withAi(provider) attaches the provider to conn.assigns.ai
import { createProvider, send, createConversation, runAgent, tool } from "@wess/atlas/ai"

const openai = createProvider({ provider: "openai", key: process.env.OPENAI_API_KEY! })

const conv = createConversation("You are a helpful assistant")
const { response } = await send(openai, conv, "Hello")

for await (const chunk of openai.chatStream({ messages: [{ role: "user", content: "Hi" }] })) {
  if (chunk.type === "text") process.stdout.write(chunk.content ?? "")
}

Works with OpenAI, Anthropic, Ollama, or any OpenAI-compatible endpoint (baseUrl override).

Templates#

Atlas ships with 10 project templates, scaffolded via atlas init --template <name>:

TemplateDescriptionKey Packages
minimalJust server + configconfig, server
apiREST API with db, auth, migrationsconfig, db, migrate, server, auth
edgeApp + TLS-terminating edge (replaces Caddy/nginx)config, server, edge
fullstackAPI + React frontendconfig, db, server, auth, ui
adminAPI + admin panelconfig, db, server, auth, admin
workerBackground job processorconfig, db, cache, cli
realtimeWebSocket + SSEconfig, server
socialnetworkUsers, posts, follows, likes, feeds, media, real-timeconfig, db, server, auth, cache, storage
cmsHeadless CMS with content types, publishing, webhooksconfig, db, server, auth, storage, admin
aiChatbot, RAG, agents, embeddings, streamingconfig, server, ai

Composition Patterns#

Typical Backend App#

// 1. Config
const config = defineConfig({ database: env("DATABASE_URL"), ... })

// 2. Database
const db = connect({ driver: "postgres", url: config.database })

// 3. Migrations
await migrate.up(db)

// 4. HTTP with pipes
serve({
  routes: [
    post("/auth/signup", signup({ db, table: "users", ... })),
    post("/auth/login", login({ db, table: "users", ... })),
    get("/api/users", pipeline(requireAuth({ secret }))(listUsers)),
    post("/api/files", pipeline(requireAuth({ secret }), parseMultipart)(uploadFile)),
  ],
})

Full Stack#

Use @wess/atlas/ui on the client, and serve the same HTML for every client path:

// server.ts
import { get, pipe, putHeader, serve, text } from "@wess/atlas/server"

const page = await Bun.file("./index.html").text()
const shell = pipe((c) => putHeader(text(c, 200, page), "content-type", "text/html; charset=utf-8"))

serve({
  routes: [...apiRoutes, get("/", shell), get("/app/*", shell)],
})
// app.tsx
import "@wess/atlas/ui/styles.css"
import { AtlasProvider } from "@wess/atlas/ui/provider"
import { createRoot } from "react-dom/client"

createRoot(document.getElementById("root")!).render(<AtlasProvider><App /></AtlasProvider>)

The admin panel needs none of this: admin() returns routes that serve its own prebuilt shell, bundle, and sign-in screen.

Skills#

skills/ holds small, task-focused instructions for coding agents, one directory per kind of work, shipped with the CLI. atlas skills install copies them into a project. They cost far less context than the references, which remain canonical, and bun run skills:verify typechecks every snippet against the source.

AGENTS.md Convention#

Each package includes an AGENTS.md at its root — a compact manifest (~80 lines) of the public API.

Structure:

  • Exports — Each public function/type with signature and return type
  • Types — Data shapes that users need to generate correct code
  • Usage — Minimal working example
  • Dependencies — Sibling and external packages

Why this matters:

LLMs can read packages/db/AGENTS.md instead of traversing 20+ source files. This is critical for token efficiency in prompt engineering and code generation workflows.

Conventions:

  • Keep under 100 lines
  • Every public export must be listed
  • Include return types and argument types
  • Update whenever the API changes
  • Treat as part of the build process

Design Philosophy#

  1. Functional — No classes, immutable data, composition over inheritance
  2. Minimal dependencies — Wrap Bun's native APIs, don't reach for external packages
  3. Composable — Each package works independently or with others
  4. AI/LLM-friendly — Clear APIs, good types, predictable patterns, AGENTS.md reference
  5. No framework lock-in — Use what you need, combine with anything else
  6. Bun-native — Idiomatic to Bun's philosophy and APIs
  7. Shallow dependencies — Max 1 level of sibling package dependencies

External Dependencies#

PackageDeps
confignone
dbzod
migratenone
servernone
authnone
securitynone
oauthnone
edgenone
emailnone
notifynone
sharenone
storagenone
cachenone
requestnone
ssonone
clinone
uireact, react-dom, @tanstack/*, lucide-react
adminreact, react-dom, @tanstack/*, lucide-react
mcpnone
ainone

Total: Only zod on the backend. Frontend uses React + TanStack + lucide-react.

Development Environment#

  • Monorepo — Bun workspaces
  • Testing — bun test across all packages
  • TypeScript — Strict mode
  • Linting — Biome (run bun run check / bun run tidy)
  • File naming — All lowercase, no dashes or underscores, subdirectories for organization
  • Environment — .env in root (Postgres on localhost, SQLite in memory for tests)
Canonical sourcedocs/overview.md
Type to search guides and package references.