Guides
Managed apps
Add custom server-side behavior with a local executable that exchanges JSON with Itsy.
How apps fit
Itsy Server serves the HTML and handles form protection. Your app handles its logic and returns data for the template. The bundled Contact app uses this model; Blog has its own built-in content editor.
Custom apps run as managed local executables. You provide their code and storage behavior.
Register an app
Build an executable that implements the contract below. Place its files in itsy/apps/example/ with an app.json manifest:
{
"name": "example",
"version": "0.1.0",
"protocol": 2,
"executable": {
"darwin-arm64": "example-darwin-arm64",
"linux-amd64": "example-linux-amd64"
}
}Include a binary for your development computer and server. Add the app to your existing configuration:
{"apps": {"example": {"mount": "/example"}}}Create site/example/index.html. Itsy launches the app and passes requests below this mount to it. Restart dev after rebuilding the executable or changing app settings.
Requests and template data
A custom app receives GET requests for pages that exist under its mount. A form POST needs {csrf} and sends its fields as flat JSON:
{"name": "Sam"}Repeated fields become arrays of strings. Itsy strips the mount from the path: /example/greet calls /greet. Its own CSRF field is removed before forwarding.
Return application/json. A 200 response can supply template values:
{"data": {"message": "Hello, Sam"}}The page can display {example.data.message}. App data is escaped and never interpreted as template code.
The response accepts six keys:
- data
- An object of template values.
- invalid
- Field names mapped to validation messages.
- error
- A failure message for the page.
- flash
- Named messages for the rendered page.
- redirect
- A mount-relative path, such as
thank-you. - session
- A cookie instruction, described below.
Use 422 with invalid for field errors, or 503 with error for an operational failure. A successful redirect becomes a browser 303. Other responses become a generic unavailable page.
Forms are limited to 32 KiB and JSON responses to 1 MiB. Unknown envelope keys are rejected; only session accepts an explicit null.
Launch and handshake
The process receives three inherited file descriptors:
- 3
- A bound loopback TCP listener for your HTTP server.
- 4
- The launch configuration as JSON.
- 5
- A parent-liveness pipe. Shut down when it reaches EOF.
{
"Name": "example",
"Token": "<private bearer token>",
"Storage": "<private storage directory>",
"CapabilityURL": "",
"CapabilityToken": ""
}The last two values are empty for custom apps. Read configuration from these descriptors rather than environment variables.
Require Authorization: Bearer <Token> on every request. Answer GET /_flea/handshake with status 200:
{"name": "example", "version": "0.1.0", "protocol": 2}The name and protocol must match the package. The handshake path retains the Flea spelling.
App-owned sessions
Your app owns session state. Itsy passes the opaque session token in X-Flea-Session. On a successful response, set or rotate it with:
{"session": {"token": "<your opaque token>", "max_age": 86400}}Use a cookie-safe token of 16 to 512 characters and a lifetime of up to 30 days. Omit session to leave it unchanged, or return session: null to clear it.
Itsy handles the host-only, Secure, HttpOnly cookie. An app session does not grant admin access.
The provided storage directory survives deployments. It is private and excluded from site backups, so arrange backups for your app's data.