Agent Skill
A packaged Agent Skill that teaches a coding assistant to write Volga correctly — the same material as this site, reorganised for a model rather than a reader.
Source: skill/volga.
Why you might want it
Assistants are confidently wrong about Volga, and for a specific reason: the 0.9 line changed security defaults and removed a family of helper methods, 0.10.0 rebuilt how requests reach middleware and how route groups apply what they hold, 0.10.1 changed what a rejected token is answered with, and 0.11.0 added synchronous handlers, catch-all routes and a shutdown timeout, 0.11.1 gave a route group a fallback of its own, 0.12.0 turned a handler's Err into an error for map_err rather than a second response, 0.13.0 made every path parameter arrive percent-decoded, 0.13.1 let a route group or a route set its own request body limit, 0.13.2 let a resource server fetch an issuer's keys from a URL of its choosing, 0.13.3 made with_alg replace the default HS256 and checks the keys against it at startup, 0.14.0 made Dc<T> read-only and checks services and path parameters at startup, and 0.14.1 renamed cache_control, use_problem_details and ValidPath after one naming rule, while most Volga code a model has seen predates all of it. So the failure mode is not a forgotten method name. It is an assistant writing ok!("hi", [("x-key", "v")]) with a comma instead of a semicolon, returning Err("not found".to_string()) and getting a 500, decoding a path parameter that arrived decoded already, reaching for with_default_cors(), calling use_cors() on an app that never configured CORS, mapping static files with map_static_assets(), or shipping a bearer-auth setup that answers 400 to every request once it sits behind a TLS-terminating proxy.
The skill front-loads exactly those traps, then routes to detail on demand.
What is in it
| File | Covers |
|---|---|
SKILL.md | Establishing the version and features, the non-negotiables, a minimal app, routing |
references/routing.md | Routes, groups, path/query params, path parameters of your own types, JSON, forms, files, multipart, body limits, headers, cookies |
references/responses.md | Response macros, IntoResponse, status codes, streaming, errors and IntoError, Problem Details |
references/middleware.md | with / wrap / attach / filter / tap_req / map_ok, CORS, compression, static files, rate limiting |
references/di-config.md | DI lifetimes, Inject, factories, configuration files, hot reload |
references/security.md | Basic auth, JWT, authorizers, OAuth 2.1 / OIDC, the client crate, TLS, HSTS |
references/realtime.md | WebSockets, WebSocket-over-HTTP/2, Server-Sent Events |
references/operations.md | Feature flags, graceful shutdown, cancellation, tracing, OpenAPI and hand-written schemas, TestServer |
references/migration.md | Symptom → cause, and the 0.14.0 → 0.14.1, 0.13.x → 0.14.0, 0.13.2 → 0.13.3, 0.13.1 → 0.13.2, 0.13.0 → 0.13.1, 0.12.x → 0.13.0, 0.11.x → 0.12.0, 0.10.x → 0.11.0, 0.10.0 → 0.10.1, 0.9.x → 0.10.x and 0.8.x → 0.9.x upgrade paths |
SKILL.md stays short on purpose: an entrypoint an agent always reads, and eight references it loads only when the task needs one.
Install
The SKILL.md format is a shared standard, so installation is the same everywhere: unzip and copy the volga/ directory into the tool's skills folder, keeping the folder name — it has to match the name in the frontmatter.
| Tool | Personal | Per project |
|---|---|---|
| Claude Code | ~/.claude/skills/volga/ | .claude/skills/volga/ |
| opencode | ~/.config/opencode/skills/volga/ | .opencode/skills/volga/ |
| Codex CLI | ~/.codex/skills/volga/ | .codex/skills/volga/ |
unzip volga-skill.zip
mkdir -p ~/.claude/skills && cp -r volga ~/.claude/skills/
Restart the assistant afterwards — skills are discovered at startup.
opencode also reads .claude/skills/ and .agents/skills/, so a single copy inside a project can serve more than one tool.
Anything else
Any assistant that can read a file will do. Point it at SKILL.md and let it follow the links, or add a line to the project's AGENTS.md:
For Rust web work with the `volga` crate, read
`.agents/skills/volga/SKILL.md` and the reference file it routes you to.
What it front-loads
The whole point of shipping a skill rather than a prose summary is the list of things a model gets wrong on its own. A sample of what SKILL.md puts before anything else:
- Custom headers come after a semicolon.
ok!("Hello"; [("x-key", "v")]). A comma matches theformat!arm and fails to compile. with_*consumes theApp,map_*borrows it. Configuration is chained first; routing needslet mut app.require_httpsandstrip_token_from_requestdefault to on for bearer authentication — the reason a working local setup returns400behind a reverse proxy.with_audmakesauda required claim unlesswithout_strict_aud()says otherwise.with_default_cors()andwith_default_tracing()are gone, and the on/off builders (with_credentials,with_preload, …) no longer take abool.Path<T>is a tuple,NamedPath<T>is the named struct, and neither may be mixed with positional path parameters in one handler. A type of your own becomes a path parameter throughFromPathArgsince 0.13.0.fullis not everything —#[derive(Claims)],#[http_header], development certificates andTestServereach need a featurefulldoes not include.- A rejected bearer token is
401, not403since 0.10.1 —403is left to a valid token that lacks the role or permission a route asks for. - An unresolvable DI graph stops the app at startup since 0.10.1, so a hand-written
impl Injectshould declare what it resolves. - A synchronous handler runs on the runtime worker — blocking work goes in
blocking, not in a plainfnhandler. - A catch-all value is not a safe file path:
{*path}arrives unnormalized,..included. - Endless responses end on the shutdown signal, via the
ShutdownHandleextractor, instead of being closed at the shutdown timeout. - The SPA shell answers
GETandHEADalone since 0.11.1, and an API sharing the server gives its group amap_fallbackof its own. - A handler's
Erris an error since 0.12.0: it reachesmap_err, a bare string answers500, and an error type implementsIntoError— neverFrom<T> for Error. - Path parameters arrive decoded since 0.13.0: a handler never decodes one again, and a literal segment written with
%XXpanics at mapping. - A body limit belongs where the body is read since 0.13.1: a route group or a route raises or lowers it for itself, the most specific limit wins, and a body over it answers
413. with_issueris theisstokens carry — when the server cannot reach it (a container, an ingress host), 0.13.2 fetches the keys fromwith_jwks_uriinstead of pointing the issuer at an internal URL.with_algnames the whole list since 0.13.3: the first call replacesHS256,encodesigns with the first, a static RSA, EC or Ed25519 key comes withwith_algof its family or the app does not start, andwith_issmakesissa required claim.Dc<T>is read-only since 0.14.0: changing state lives behind aMutex, anRwLockor an atomic inside the service, and a handler or middleware taking a service nobody registered stops the app at startup.- A handler reads only the path parameters its route declares since 0.14.0:
map_get("/sum", |x: i32, y: i32| ..)panics where it is mapped, instead of answering500on every request.
The code in it compiles
The Rust in the skill is compiled against the published volga crates in this repository's CI, on the same job that checks the snippets on this site — so what an assistant copies out of it builds. Blocks that show a shape rather than a program (a signature, a trait impl with elided bodies) opt out explicitly.
Run the same check yourself after editing:
python3 ci/check-snippets.py --docs-dir skill --default-mode compile-fragment --default-features "full auth-full macros dev-cert test"
Version
The skill tracks volga 0.14.1 on MSRV 1.90. The frontmatter records both, so an assistant can tell whether the skill matches the crate in front of it:
metadata:
volga-version: "0.14.1"
msrv: "1.90"