Rust framework that compiles entire infrastructure into one binary
Forge - the standard library for agent-built SaaS
Every app needs the same plumbing, and the usual answer is a separate service for each piece:
- Redis for caching and sessions
- a queue for background jobs
- object storage for uploads
- an auth service for logins and sessions
- a cron runner for scheduled work
- a rate limiter
Forge does all of it in one library, with the same API in Rust, Node, and Python.
One connection, eight primitives
npm install forgelib
Configuration lives in a forge.toml at your project root. init() reads it and instantiates the runtime; string values may reference the environment as ${VAR}:
[postgres]
url = "${DATABASE_URL:-}"
embedded = true
Set DATABASE_URL to use a managed or shared Postgres. Leave it unset and Forge downloads and runs Postgres for you (data persists in .forge/pg) โ built into the Node and Python packages, behind the embedded cargo feature in Rust. Forge supports Postgres 17+.
Omit settings you do not need; Forge applies production-safe defaults for the rest.
import { ForgeClient } from "forgelib";
const forge = await ForgeClient.init(); // instantiates the runtime from ./forge.toml
// auth: argon2 password hashing and sessions const hash = await forge.hashPassword(password); const session = await forge.createSession(userId);
// rate limit: 20 attempts per minute, keyed by email const limit = await forge.rateLimitCheck("login", email, 20, 60); if (!limit.allowed) throw new Error("slow down");
// key/value: TTL cache and atomic counters await forge.kv<typeof profile>(user:${userId}).set(profile, { ttlSeconds: 3600 }); const views = await forge.kvIncr(views:${userId}, 1);
// queue: background jobs await forge.queue<{ to: string }>("emails").enqueue({ to: email });
// pub/sub: fan an event out to subscribers await forge.topic<{ userId: string }>("user.created").publish({ userId });
// blob: store a file, hand back a link that expires in an hour await forge.blobPut(exports/${userId}.csv, csv, "text/csv"); const link = await forge.blobPresignDownload(exports/${userId}.csv, 3600);
// schedule: recurring work on a cron await forge.scheduleCron("nightly-report", "0 0 *", "reports", "{}");
// feature flags: roll a feature out to 25% of users await forge.setFlagPercent("new-ui", 25); const newUi = await forge.flag("new-ui", false, userId);
The same in Rust
cargo add forgelib
use std::time::Duration;
use forgelib::{
Forge, Bytes, SetOpts, EnqueueOpts, PutOpts,
ScheduleOpts, SessionOpts, Limit, FlagRule, EvalCtx,
};
let forge = Forge::init().await?; // instantiates the runtime from ./forge.toml
// auth let hash = forge.auth().hash_password(&password).await?; let session = forge.auth().createsession(&userid, SessionOpts::default()).await?;
// rate limit let limit = forge.ratelimit() .check("login", &email, Limit::perduration(20, Duration::fromsecs(60))) .await?;
// key/value forge.kv().set(&format!("user:{user_id}"), profile.into(), SetOpts::new().withttl(Duration::fromsecs(3600))).await?; let views = forge.kv().incr(&format!("views:{user_id}"), 1).await?;
// queue forge.queue().enqueue("emails", payload.into(), EnqueueOpts::new()).await?;
// pub/sub forge.pubsub().publish("user.created", Bytes::from(event)).await?;
// blob forge.blob().put(&key, body, PutOpts::new()).await?; let link = forge.blob().presigndownload(&key, Duration::fromsecs(3600)).await?;
// schedule forge.schedule().cron("nightly-report", "0 0 *", "reports", Bytes::new(), ScheduleOpts::new()).await?;
// feature flags forge.config().set_flag("new-ui", FlagRule::Percent(25)).await?; let newui = forge.config().flag("new-ui", false, &EvalCtx::user(userid)).await;
The same in Python
pip install forgelib
import forgelib
forge = await forgelib.ForgeClient.init() # instantiates the runtime from ./forge.toml
auth
hash = await forge.hash_password(password)
session = await forge.createsession(userid)
rate limit
limit = await forge.ratelimitcheck("login", email, 20, 60)
if not limit.allowed:
raise RuntimeError("slow down")
key/value
await forge.kv(f"user:{userid}").set(profile, ttlseconds=3600)
views = await forge.kvincr(f"views:{userid}", 1)
queue
await forge.queue("emails").enqueue({"to": email})
pub/sub
await forge.topic("user.created").publish({"userid": userid})
blob
await forge.blobput(f"exports/{userid}.csv", csv, "text/csv")
link = await forge.blobpresigndownload(f"exports/{user_id}.csv", 3600)
schedule
await forge.schedule_cron("nightly-report", "0 0 *", "reports", "{}")
feature flags
await forge.setflagpercent("new-ui", 25)
newui = await forge.flag("new-ui", False, userid)
What you get
| Primitive | What it does | | --- | --- | | key/value | get, set, mget, incr, compare-and-swap, prefix scan, TTLs | | queue | enqueue and dequeue with acks, retries, delays, dedup, depth | | pub/sub | publish and subscribe on topics | | blob | put, get, delete, presigned upload and download URLs | | auth | password hashing, sessions, API keys | | rate limit | token-bucket or sliding-window checks | | schedule | cron and one-off jobs | | config | settings and feature flags with percentage rollout |
By default every primitive runs on one Postgres database, so there's nothing else to operate. When one needs to scale, move that piece onto its own backend without changing your code.
Test on memory, ship on Postgres
The memory and Postgres backends pass the same conformance suite, so tests run in-process with no database and behave the way production will. Pick the backend in forge.toml, using a ${VAR} reference so the same file flips between tests and production from the environment.
[backends]
default = "${FORGEBACKEND:-postgres}" # FORGEBACKEND=memory in tests
[postgres] url = "${DATABASE_URL:-}" embedded = true
Build it with an agent
Forge's API isn't in any model's training data, so a coding agent won't know it cold. Install the forge-idiomatic-developer skill so your agent learns the conventions before it writes any Forge code.
npx skills add isala404/forge
It teaches which primitive fits which task and the idioms for each language, so you get working code instead of guesses.
Not in scope
- The APIs are a shared subset, not a full reimplementation. key/value covers the Redis commands most apps use, not Lua scripts or sorted sets. blob does CRUD and presigned URLs, not S3 lifecycle rules.
- auth is the essentials: password hashing, sessions, and API keys, enough to have working auth in minutes while you prototype. For OAuth, 2FA, or email verification, bring Clerk, Firebase, or a dedicated provider.
- Forge ships two backends, memory and Postgres, plus local disk for blob. If you need Redis or S3, implement the primitive's trait and inject it, or use that service directly.
- Not an ORM or a web framework. Your tables and routes stay yours.
Examples
Three full apps in examples/, each built on the Rust, Node, and Python backends with a React frontend.
- todo: accounts, lists, and a background audit queue
- links: a URL shortener with click counts, QR images, and analytics
- chat: real-time messaging with presence
License
MIT. Do whatever you want.
Postgres is enough.
Quick Start ยท Examples ยท Discussions