From isala404-forge
Provides verified, idiomatic API calls for the forgelib library (Rust, Node, Python) across eight backend primitives: kv, queue, pubsub, blob, auth, ratelimit, schedule, config. Activates when forge.toml or forgelib imports are detected.
How this skill is triggered — by the user, by Claude, or both
Slash command
/isala404-forge:forge-idiomatic-developerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Forge is one library that gives an app eight backend primitives on a single Postgres
Forge is one library that gives an app eight backend primitives on a single Postgres
connection, with the same behavior in Rust, Node, and Python. You call init() once,
which reads a forge.toml, and every primitive hangs off the returned client.
The API is small and consistent, but the exact names differ per language and are not
in any model's training data. Do not invent method names. Use the tables in this
skill, or read bindings/node/client.d.ts and bindings/python/src/lib.rs in the
repo — those files are the contract.
Match the job to the primitive before writing anything. Reaching for the wrong one (pub/sub for durable work, a queue for a cache) is the most common mistake.
| You need to… | Use | Never use it for |
|---|---|---|
| Cache a value, hold session-ish data, count, dedup, compare-and-swap | kv | Large blobs; use blob |
| Run work in the background, retry on failure, delay, dead-letter | queue | Fire-and-forget notifications |
| Fan a live event out to connected clients (presence, typing, dashboards) | pubsub | Anything that must not be lost — it is at-most-once |
| Store and serve files, hand out presigned upload/download URLs | blob | Small hot values; use kv |
| Hash passwords, issue and validate sessions, mint API keys | auth | Rolling your own crypto |
| Throttle by key (login attempts, per-user API calls) | ratelimit | Durable counters; use kv |
| Run a cron, or enqueue one job at a future time | schedule | Immediate work; enqueue directly |
| Store runtime settings and evaluate feature flags with rollout | config | Secrets that belong in the environment |
Two rules that catch people:
Same primitives, three surface styles. The raw contract methods carry strings/bytes 1:1 across languages; Node and Python also expose native JSON handles on the main client so app payloads are real objects without a second import path.
Rust — namespaced accessors plus option-struct builders. Fallible calls return
Result, so ? them.
use std::time::Duration;
use forgelib::{Forge, SetOpts};
let forge = Forge::init().await?; // reads ./forge.toml
forge.kv().set("k", "v".into(), SetOpts::new().with_ttl(Duration::from_secs(60))).await?;
let n = forge.kv().incr("hits", 1).await?;
Node — flat camelCase methods on the client, plain positional arguments, optional
trailing args passed as null to skip. Everything is async.
import { ForgeClient } from "forgelib";
const forge = await ForgeClient.init(); // reads ./forge.toml
await forge.kvSet("k", "v", 60); // ttlSeconds
const n = await forge.kvIncr("hits", 1);
Python — flat snake_case methods, every one awaitable, optional args default to
None.
import forgelib
forge = await forgelib.ForgeClient.init() # reads ./forge.toml
await forge.kv_set("k", "v", 60) # ttl_seconds
n = await forge.kv_incr("hits", 1)
Full per-language method tables and idioms:
ForgeClient methods + native JSON handles.Two idioms to reach for by default (full examples live in the references above):
forge.queue(name) / forge.kv(key) /
forge.config(key, default) / forge.topic(name) return typed handles in Node and
Python; Rust re-exports typed handles from the crate root.forge.worker(queue, handler, { signal }) (abort to drain), Python
forge.worker(queue, handler, stop=event), Rust forge.worker(queue).run(handler).
It dequeues, heartbeats at a third of the visibility window, acks on success, nacks
on a thrown error, and abandons the job if the lease is lost. If you must hand-roll,
heartbeat before the lease expires or the job is redelivered mid-flight.One file at the project root configures the whole runtime. init() reads it, applies
production-safe defaults for anything omitted, and migrates its own tables. An unknown
key is a startup error, not a silent typo.
[postgres]
# A set url wins; when it resolves empty, embedded = true downloads and runs a
# local PG 17 (data persists in .forge/pg) — no Postgres install needed.
url = "${DATABASE_URL:-}"
embedded = true
[backends]
default = "${FORGE_BACKEND:-postgres}" # set FORGE_BACKEND=memory in tests
[blob]
signing_secret = "${FORGE_BLOB_SIGNING_SECRET:-}" # required for presigned URLs
${VAR} / ${VAR:-default} interpolation runs on string values only (numbers
and booleans stay literal). A ${VAR} with no value and no default is a hard error,
so a missing secret stops startup instead of resolving to "".backends.default is the memory-vs-postgres switch. Drive it from the
environment (${FORGE_BACKEND:-postgres}) so the same file runs on memory
in tests (in-process, no database) and postgres in production. Both pass the same
conformance suite, so behavior matches.[blob].signing_secret. CRUD works without it; the
presign methods fail without it.[forge].namespace prefixes every key/queue/topic so several apps can share one
database. It must not contain a colon.Every failure maps to one canonical code. Same set across languages; the surface differs.
| Code | Meaning | Retryable |
|---|---|---|
NOT_FOUND | The entity does not exist | No |
INVALID | Caller bug: bad argument, malformed key, out-of-range option | No |
LIMIT | A size/length/quota ceiling was exceeded | No |
PRECONDITION | CAS mismatch, lost lease, duplicate dedup id — re-read state and decide | No |
UNAVAILABLE | Transient backend outage (pool timeout, dropped connection) | Yes |
CONFIG | Misconfiguration; only ever raised from init() | No |
BACKEND | A backend error that is none of the above | Sometimes |
Error's message, e.g.
"PRECONDITION: ..." (a retryable backend error reads "BACKEND(retryable): ...").
Parse it with forgeErrorCode(err) / test retryability with
forgeErrorRetryable(err) from forgelib.Error
(InvalidError, UnavailableError, …, all subclassing ForgeError), each
carrying a retryable attribute. Use forge_error_code(exc) /
forge_error_retryable(exc) from forgelib.Err(forgelib::ForgeError); match the variant, or call
.is_retryable().Only UNAVAILABLE (and a BACKEND error flagged retryable) is worth retrying.
Retrying an INVALID or PRECONDITION just fails again.
kvIncr returns a JS number (f64) in Node, so a counter past 2^53 loses
precision. Real counters never get there; if yours might, read it back losslessly
with forge.kvGetBytes(). Python's kv_incr returns an exact int, Rust's an i64.kvGet / blobGet (and Python kv_get) decode
bytes as UTF-8 with replacement. For binary values use the byte variants:
Node forge.kvGetBytes() / forge.kvSetBytes() / forge.blobGetBytes() /
forge.blobPutBytes(); Python forge.kv_get_bytes() / forge.kv_set_bytes(), and
in Python forge.blob_put() / forge.blob_get() are already bytes-native (there is
no blob_*_bytes; use forge.blob_put_object() when you also need metadata).receipt (delivery-unique), never its id (stable across
redeliveries — that is your idempotency key). A receipt only settles from the same
client that leased it.runSchedulerOnce / run_scheduler_once and
maintain on an interval (e.g. every 30s). In Rust use the maintenance loop.
Without a tick, crons never fire and expired rows never get swept.memory backends are per-process. For pub/sub and rate limit that means no
cross-replica delivery, so memory is for tests only — Forge logs a warning if you
run those two on memory outside tests. Use postgres in any multi-replica deploy.Every method you call must exist in the binding. If you are unsure of a name, grep the
contract (bindings/node/client.d.ts, bindings/python/src/lib.rs, src/lib.rs) or
the per-language reference here. The repo's tools/skill-check guard verifies the
names in this skill against those files, so a name here is real for the committed API.
npx claudepluginhub joshuarweaver/cascade-code-general-misc-1 --plugin isala404-forgeForge schema-bound, sandboxed tools at runtime to avoid repeating Bash snippets, compute precise numerical results, or produce deterministic file output.
Provides expert guidance for next-forge Turborepo monorepo SaaS starter by Vercel. Useful when scaffolding via npx next-forge init or editing @repo workspace packages.
Guides full-stack backend architecture and frontend-backend integration with patterns for API design, auth, error handling, real-time features, and production hardening.