Skip to content
Get Started

Security & Authentication

Zebric includes built-in security features for authentication, authorization, and request protection.

Enable email/password authentication:

[auth]
providers = ["email"]
trustedOrigins = ["http://localhost:3000"]

Authentication is powered by Better Auth with server-side session management.

Control authentication requirements per page:

[page."/dashboard"]
title = "Dashboard"
auth = "required" # Must be logged in
layout = "dashboard"
[page."/posts"]
title = "Public Posts"
auth = "none" # No login needed
layout = "list"
[page."/profile"]
title = "My Profile"
auth = "optional" # Shows user info if logged in
layout = "detail"

The auth layout renders sign-in and sign-up forms:

[page."/sign-in"]
title = "Sign In"
layout = "auth"

Define CRUD permissions on entities:

[entity.Post.access]
read = true # Anyone can read
create = true # Anyone can create
update = { authorId = "$currentUser.id" } # Only author can update
delete = { "$currentUser.role" = "admin" } # Only admins can delete
PatternDescription
{ field = "value" }Field equals value
{ field = "$currentUser.id" }Field matches current user
{ "$currentUser.role" = "admin" }Current user has role
{ or = [...] }Any condition matches
{ and = [...] }All conditions match
# Authors can see their own drafts; everyone sees published posts
[entity.Post.access]
read = { or = [{ status = "published" }, { authorId = "$currentUser.id" }] }

Restrict read/write on individual fields:

[entity.User]
fields = [
{ name = "email", type = "Email", access = { read = true, write = { "$currentUser.role" = "admin" } } },
{ name = "salary", type = "Integer", access = { read = { "$currentUser.role" = "admin" }, write = false } }
]

For agents, REST API clients, or external integrations. API keys apply to skill routes only (routes registered from [skill.*] actions) — standard page routes and /api/auth/* are unaffected.

[[auth.apiKeys]]
name = "dispatch-agent"
keyEnv = "DISPATCH_AGENT_API_KEY"
FieldDescription
nameHuman-readable identifier for the key. Used as the actor ID in sessions, audit events, and logs.
keyEnvName of the environment variable holding the secret value. The key itself is never stored in the blueprint.

Set the env var before starting the server:

Terminal window
export DISPATCH_AGENT_API_KEY="sk-your-secret-key-here"

If the env var isn’t set at startup, the key is skipped and a warning is logged — the server still starts normally.

API key requests skip CSRF validation but still go through access control. Send the key in the Authorization header:

Terminal window
curl -X POST http://localhost:3000/api/issues/01J.../status \
-H "Authorization: Bearer sk-your-secret-key-here" \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}'
  1. At startup, the runtime reads auth.apiKeys from the blueprint, resolves each keyEnv from process.env, and stores the resolved keys in memory.
  2. When a request hits a skill route, the runtime checks the Authorization header before falling back to session-based auth.
  3. If the bearer token matches a configured key, a synthetic session is created with user.id and user.name set to the key’s name field. This session flows through to workflows, audit events, and access control like any regular user session:
{
"id": "apikey-dispatch-agent",
"userId": "dispatch-agent",
"user": { "id": "dispatch-agent", "name": "dispatch-agent", "email": "" }
}

So {{ variables.data.user.id }} in workflow templates resolves to the key name (e.g. dispatch-agent), which becomes the actorId in audit events.

Keys are checked via exact string match in memory — there’s no hashing step, so treat the env var as a secret. Rotate keys by changing the env var and restarting the server.

Zebric applies these security measures automatically:

  • CSRF protection on all state-changing requests (POST, PUT, DELETE)
  • Security headers (CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
  • Open redirect prevention on login redirect flows
  • Path traversal protection on file resolution
  • HTML entity encoding to prevent XSS
  • Workflow body filtering to prevent injection in workflow payloads
  • Audit logging for security-relevant events
  • Error sanitization to avoid leaking internal details
[auth.session]
duration = 86400 # Session duration in seconds (default: 24 hours)
idle_timeout = 3600 # Idle timeout in seconds