Production is our dev environment.
Paste this into your AI agent (Claude, Cursor, Copilot, any agent with terminal access):
Download and follow the steps from https://github.com/hazat/manifest/blob/main/SPARK.md
That's it. Your agent becomes a setup guide called Spark that walks you through cloning, configuring, and building your first feature.
Want to read what SPARK.md does before pasting? Smart. Click here.
SPARK.md is a prompt that turns your agent into a Manifest onboarding guide. It will:
- Check that Bun and git are installed
- Clone the repo and make it yours
- Help you rename the project
- Verify the server runs and tests pass
- Walk you through building your first feature
- Hand off once you're set up
No telemetry. No install scripts. No magic. It's a markdown file with instructions your agent follows step by step. Read it yourself: SPARK.md
The first framework where the agent isn't using the framework — it is the framework. Manifest is source code the whole way down. No npm package, no hidden runtime, no abstractions the agent can't read. The framework ships inside your project as ~1,000 lines of TypeScript that the agent wrote, understands, and evolves alongside your application.
Built on Bun. No build step. No magic.
bun --hot index.ts
That's the entire deployment. The agent edits a file, Bun hot-reloads it, the next request runs the new code. In production.
Every framework today was designed for humans to write code and machines to run it. Manifest inverts this. It's designed for agents to write code, read code, and fix code — with humans steering.
The problem with existing frameworks:
- Next.js hides behavior behind file-system conventions, bundler transforms, and server/client boundaries that even experienced developers struggle to trace. An agent has no chance.
- Express/Fastify scatter a single behavior across routes, middleware, validators, error handlers, and services. To understand one endpoint, you need to read six files and know the registration order.
- NestJS wraps everything in decorators and dependency injection. The actual execution flow is invisible — it lives in the framework's reflection metadata, not in your code.
- Laravel/Rails rely on convention-over-configuration so heavily that understanding what happens on a request requires knowing the framework's internals by heart.
These frameworks optimize for developer ergonomics. Manifest optimizes for agent comprehension. It turns out these are the same thing — explicit code that you can read linearly is good for everyone.
1. One feature, one file.
A feature file contains everything: route, input validation, authentication, business logic, side effects, error cases. No hunting across directories. An agent reads one file and knows everything about one behavior.
2. Explicit over elegant.
If something happens, it's because the code says so. Not because a middleware was auto-discovered, a decorator triggered a side effect, or a bundler rewrote your imports. Verbose is correct. Terse is suspicious.
3. Self-describing code.
Every feature carries machine-readable metadata: what it does, what it depends on, what it affects. The codebase is its own documentation. An agent dropping in cold reads MANIFEST.md and orients itself in seconds.
// features/UserRegistration.ts
import { defineFeature, t } from '../manifest'
import { db } from '../config/database'
import { users } from '../schemas/users'
import { mailer } from '../services/mailer'
import { eq } from 'drizzle-orm'
export default defineFeature({
name: 'user-registration',
description: `Creates a new user account. Validates email uniqueness,
hashes password, persists user, and sends a welcome email.
Returns the created user without sensitive fields.`,
route: ['POST', '/api/users/register'],
authentication: 'none',
rateLimit: '5/minute/ip',
sideEffects: [
'Inserts one row into users table',
'Sends welcome email via mailer service',
],
errorCases: [
'409 - Email already registered',
'422 - Validation failed',
],
input: {
email: t.string({
description: 'User email address. Must be unique across all users.',
required: true,
format: 'email',
}),
password: t.string({
description: 'Account password. Hashed with argon2 before storage.',
required: true,
minLength: 8,
maxLength: 128,
}),
displayName: t.string({
description: 'Public display name shown to other users.',
required: true,
maxLength: 100,
}),
},
async handle({ input, ok, fail }) {
const existing = await db
.select()
.from(users)
.where(eq(users.email, input.email))
.limit(1)
if (existing.length > 0) {
return fail('Email already registered', 409)
}
const [user] = await db.insert(users).values({
email: input.email,
password: await Bun.password.hash(input.password),
displayName: input.displayName,
}).returning({
id: users.id,
email: users.email,
displayName: users.displayName,
})
await mailer.send({
template: 'welcome',
to: user.email,
context: { displayName: user.displayName },
})
return ok('User registered', { data: user, status: 201 })
},
})From this single file, an agent knows:
- What the feature does and why (description)
- What URL triggers it (route, method, auth, rate limit)
- Every input field with types, constraints, and purpose
- Every side effect, declared before the logic
- Every error case with HTTP status codes
- The full execution flow — linear, no hidden branches
- All dependencies via explicit imports at the top
No base classes. No decorators. No inheritance. The plain object is the feature.
Most frameworks are black boxes behind node_modules/. Manifest is different:
manifest/
├── server.ts # Bun.serve() wrapper — 109 lines
├── feature.ts # defineFeature() — 83 lines
├── types.ts # t.string(), t.integer(), etc. — 141 lines
├── validator.ts # Input validation — 92 lines
├── router.ts # HTTP route matching — 76 lines
├── envelope.ts # Response formatting — 65 lines
├── scanner.ts # Feature directory scanner — 33 lines
├── testing.ts # Test client — 73 lines
├── index.ts # Barrel exports — 31 lines
└── cli/ # serve, index, check, make:feature — 352 lines
1,055 lines total. An agent reads the entire framework in seconds. It doesn't just use Manifest — it understands Manifest. When something breaks, the agent doesn't search Stack Overflow. It reads manifest/router.ts (76 lines) and fixes the routing. It reads manifest/validator.ts (92 lines) and adds a new validation rule.
The agent builds the framework as it builds the application. They're the same codebase. There is no boundary between "framework code" and "application code" — just code the agent wrote, reads, and evolves.
// index.ts
import { createManifestServer } from './manifest'
const server = await createManifestServer({
projectDir: import.meta.dir,
port: 8080,
})Under the hood: scan features/ → build route table → start Bun.serve(). That's it. No initialization ceremony, no providers, no bootstrapping phase.
{
"status": 201,
"message": "User registered",
"data": {
"id": "a1b2c3",
"email": "user@example.com",
"displayName": "Jane"
},
"meta": {
"feature": "user-registration",
"request_id": "req_xyz789",
"duration_ms": 42
}
}Every response includes meta.feature — when something breaks, the agent knows exactly which file to open.
import { createTestClient } from '../manifest/testing'
const client = createTestClient({ featuresDir: './features' })
const result = await client.call('user-registration', {
email: 'jane@example.com',
password: 'secure-password-123',
displayName: 'Jane',
})
expect(result.status).toBe(201)Call features by name. No HTTP overhead. No port conflicts. Fast, deterministic tests.
Every Manifest project has a MANIFEST.md at the root. Auto-generated. Always in sync. It's the first thing any agent reads:
## Feature Index
| Name | Route | Type | Description |
|------|-------|------|-------------|
| user-registration | POST /api/users/register | request | Creates new account |
| user-login | POST /api/users/login | request | Authenticates user |
| post-feed | GET /api/posts/feed | stream | Real-time post stream |bun run manifest index # Regenerate MANIFEST.md
bun run manifest check # Validate conventionsThis is what Manifest is built for:
Error tracker fires alert
↓
Agent reads MANIFEST.md → orients
↓
Agent reads the feature file → understands the full behavior
↓
Agent reads manifest/validator.ts → understands how validation works
↓
Agent edits the feature file → fixes the bug
↓
Bun hot-reloads → new code is live
↓
Agent commits with [agent] prefix → traceable
↓
Next request works
No deploy. No CI. No restart. The agent edits a .ts file and it's live. Because there's no build step. Because Bun runs TypeScript natively. Because --hot reloads modules without dropping connections.
Every design decision in Manifest exists to make this loop fast, safe, and reliable:
- One file per feature → the agent edits one file, not six
- Explicit side effects → the agent knows the blast radius before touching anything
- Machine-readable metadata → the agent doesn't guess, it reads
- Source-code framework → the agent can fix the framework itself
- No hidden behavior → the agent's mental model matches reality
manifest-app/
├── MANIFEST.md # Auto-generated. The agent reads this first.
├── manifest/ # THE FRAMEWORK. Source code. ~1,000 lines.
├── features/ # One file per behavior. This IS the app.
│ ├── UserRegistration.ts
│ ├── UserLogin.ts
│ └── ListPosts.ts
├── schemas/ # Drizzle ORM. One file per table.
├── services/ # Plain exported functions. No DI container.
├── config/ # Typed TypeScript. No YAML, no .env magic.
├── tests/ # Mirrors features/ 1:1.
├── index.ts # Entry point. 4 lines.
└── package.json
The features/ directory is the application. Everything else is infrastructure.
| Layer | Choice | Why |
|---|---|---|
| Runtime | Bun | Native TypeScript, built-in HTTP server, hot reload, test runner, password hashing — all in one binary |
| Language | TypeScript (strict) | Type safety without a compilation step on Bun |
| ORM | Drizzle | SQL-like, explicit, TypeScript-native. What you write is what runs |
| API style | JSON REST + SSE | No GraphQL complexity. Predictable envelopes |
| Build step | None | .ts files run directly. The source code is the production code |
| Framework | Source code | Not a dependency. Lives in manifest/. The agent can read and modify every line |
| Framework | Problem for agents |
|---|---|
| Next.js | File-system routing, bundler transforms, server/client split — too much hidden behavior |
| Express | Middleware chains, scattered route handlers — one behavior spans many files |
| NestJS | Decorators, DI containers, reflection — execution flow is invisible |
| Hono | Better than Express, but still middleware-based — agent can't see the full picture in one file |
| Elysia | Decorator-heavy, method chaining — behavior emerges from composition, not declaration |
Manifest has no middleware. No decorators. No DI container. No file-system routing. No convention-based auto-discovery. Every behavior is a defineFeature() call in a single file that the agent reads top to bottom.
bun run manifest serve # Start server (use bun --hot for live reload)
bun run manifest index # Regenerate MANIFEST.md
bun run manifest check # Validate conventions
bun run manifest make:feature Name # Scaffold a new featureThe CLI is 352 lines of TypeScript. No framework. Just process.argv.
Paste this into your agent:
Download and follow the steps from https://github.com/hazat/manifest/blob/main/SPARK.md
Your agent becomes Spark — a Manifest onboarding guide that walks you through cloning, setting up, and building your first feature step by step.
# 1. Clone the repo:
git clone https://github.com/hazat/manifest.git my-app
cd my-app
# 2. Make it yours:
rm -rf .git
git init
git add -A
git commit -m "Initial commit of my-app"
bun install
# Start developing
bun --hot index.ts
# In another terminal
curl http://localhost:8080/api/hello?name=World{
"status": 200,
"message": "Hello, World!",
"data": { "greeting": "Hello, World!" },
"meta": { "feature": "hello-world", "request_id": "...", "duration_ms": 0.42 }
}bun run manifest make:feature CreatePost --route="POST /api/posts" --auth=requiredThis scaffolds features/CreatePost.ts with the full structure and TODO markers. Fill in the blanks.
bun test45 tests across the framework and application. All using Bun's built-in test runner.
Manifest doesn't install from npm. You clone the repo and start building.
git clone https://github.com/hazat/manifest.git my-project
cd my-project
rm -rf .git
git init && git add -A && git commit -m "Initial commit of my-project"
bun install
bun --hot index.tsThe manifest/ directory is now your framework. Your agent reads it, modifies it, extends it as the application grows. There's no upstream dependency to keep in sync, no version pinning, no breaking changes from a package update.
This is intentional. Traditional frameworks maintain a boundary: framework code lives in node_modules/, application code lives in src/. You depend on the framework. You can't change it. When it breaks, you wait for a patch.
Manifest erases that boundary. The framework code lives in your project, committed to your repo, understood by your agent. When the router needs a new matching rule, the agent edits manifest/router.ts. When validation needs a new format, the agent edits manifest/validator.ts. No pull request to an upstream repo. No waiting.
What about upstream improvements?
When the Manifest base repo adds a new capability, you don't git merge. Your agent reads the upstream diff, understands the intent, and implements the idea in the context of what your project has become. That's how agents work. They don't need git merge, they need context. The upstream repo serves as a reference and inspiration, not a dependency.
The repo includes a Dockerfile, docker-compose.yml, and docker-entrypoint.sh. One command:
docker compose up --buildYour app runs on port 8080. That's it.
| Variable | Default | Purpose |
|---|---|---|
PORT |
8080 |
App server port |
NODE_ENV |
production |
Environment mode |
SPARK_WEB_TOKEN |
(empty) | Auth token for Spark dashboard |
SPARK_WEB_PORT |
8081 |
Spark dashboard port |
ANTHROPIC_API_KEY |
(empty) | API key for Spark's AI agent |
Without SPARK_WEB_TOKEN, the container runs just your app. With it, you also get the Spark web dashboard on port 8081 — an AI sidekick that watches your running app and investigates errors in real time.
🔥 Want Spark in production? Set both
SPARK_WEB_TOKENandANTHROPIC_API_KEYin your environment. The entrypoint script starts the Spark sidecar automatically — no extra containers, no extra config.
Extensions are shared functionality that follows Manifest conventions. Authentication, payment processing, email — things many projects need and shouldn't reinvent.
An extension is just a directory of features, schemas, and services. No plugin API. No hooks. No registration. Just more files following the same rules.
extensions/
├── auth/
│ ├── EXTENSION.md # Agent reads this to understand the extension
│ ├── features/
│ │ ├── Login.ts # Standard defineFeature() — same as any feature
│ │ ├── Register.ts
│ │ ├── RefreshToken.ts
│ │ └── ResetPassword.ts
│ ├── schemas/
│ │ └── sessions.ts # Standard Drizzle schema
│ └── services/
│ └── jwt.ts # Plain exported functions
│
├── stripe/
│ ├── EXTENSION.md
│ ├── features/
│ │ ├── CreateCheckout.ts
│ │ └── HandleWebhook.ts
│ └── services/
│ └── stripe.ts
bun run manifest extension install <source> # Install an extension
bun run manifest extension list # List installed extensions
bun run manifest extension make <name> # Scaffold a new extension
bun run manifest index # Rebuild the manifestThe scanner picks up features from extensions/*/features/. They show up in MANIFEST.md. They follow the same conventions. bun manifest check validates them the same way.
| Extension | What it does |
|---|---|
manifest-frontend-static |
HTML + Tailwind + vanilla TS (content sites, landing pages) |
manifest-frontend-reactive |
SolidJS + Tailwind (interactive apps, dashboards) |
manifest-drizzle-postgres |
Drizzle ORM + Postgres (database access, migrations) |
manifest-content-blog |
Markdown blog with static HTML output |
manifest-sse-example |
SSE streaming demo with frontend guides |
Every extension has an EXTENSION.md — the agent's guide to the extension, like MANIFEST.md is the guide to the app:
# Auth Extension
Provides user authentication with JWT tokens and session management.
## Features
| Name | Route | Description |
|------|-------|-------------|
| login | POST /api/auth/login | Authenticates user, returns JWT |
| register | POST /api/auth/register | Creates account |
| refresh-token | POST /api/auth/refresh | Refreshes expired JWT |
## Schemas
- sessions: Active JWT sessions with expiry tracking
## Services
- jwt: Token signing and verification (RS256)
## Configuration Required
Add to config/manifest.ts:
jwtSecret: Bun.env.JWT_SECRET
jwtExpirySeconds: 3600
## Side Effects
- Inserts/updates sessions table
- No external API callsExtensions follow the same three rules:
- One feature, one file. An auth extension's
Login.tsis a complete, self-contained feature — same as any feature you write yourself. - Explicit over elegant. No auto-registration, no decorator scanning. The scanner reads the directory. You can see what's loaded.
- Self-describing.
EXTENSION.mdtells the agent everything. No reading source code to understand what an extension provides.
And because extensions are source code in your project, your agent can modify them too. If the auth extension's password policy doesn't fit your needs, the agent edits extensions/auth/features/Register.ts. It's just a file.
Manifest is not a framework for writing web applications. It's a framework for agents writing web applications.
The distinction matters. When an agent is the primary author:
-
Code must be self-describing. Not through comments, but through structure. Every feature declares its purpose, its inputs, its side effects, its error cases — as data, not as documentation that drifts.
-
The framework must be transparent. An agent that can't read the framework can't fix the framework. Manifest is 1,055 lines of source code in your project. The agent has read every line. It wrote most of them.
-
Hot reload must be real. Not "restart the process" real. Module-level hot swap real. The agent patches a file and the next request uses the new code. No deploy pipeline. No container rebuild. Edit → live.
-
The project must be self-contained. No hidden behavior in
node_modules. No framework version upgrades that change semantics. The framework evolves with the application because they're the same codebase.
Traditional frameworks ask: "How do we make developers productive?"
Manifest asks: "How do we make the codebase fully comprehensible to an agent that can read, write, and deploy code in a single loop?"
The answer turns out to be the same things good engineering has always valued: explicitness, small files, declared dependencies, no magic. Manifest just takes it seriously.
Manifest is in active development. The core framework and key extensions are complete:
- ✅ Feature system (
defineFeature, typed inputs,ok/failhelpers) - ✅ Input validation (types, formats, constraints)
- ✅ HTTP server (Bun.serve, routing, path params, JSON envelopes)
- ✅ SSE streaming (
type: 'stream'features withemit/close/fail) - ✅ Test client (call features by name, no HTTP; stream testing)
- ✅ CLI (serve, index, check, make:feature, frontend, extensions, doctor)
- ✅ MANIFEST.md generation
- ✅ Convention validation
- ✅ Frontend support (static + reactive presets via extensions)
- ✅ Drizzle ORM + Postgres (via
manifest-drizzle-postgresextension) - ✅ Extensions ecosystem (make, install, list, scanner)
Coming next:
- ⬜ Authentication & authorization
- ⬜ Rate limiting
- ⬜ Event-triggered features
- ⬜ Agent sidecar (error tracking integration, self-healing loop)
- ✅ Docker deployment
Production is our dev environment.