General configuration server.
Highly on development stage, expect breaking changes. Feedback and contributions are very welcome!
docker run -d --name pika -v pika:/data -p 8080:8080 ghcr.io/rakunlabs/pika:latestOpen http://localhost:8080 to access the web UI.
- Multi-format configs (JSON, YAML, TOML)
- Version history with semver constraints
- Variants for environment-specific overrides
- Config inheritance (internal files, Vault, HTTP, Kubernetes, Consul, etcd, AWS, GCP, Azure)
- Token-based access control with glob scopes
- Full-text search across all configs
- Built-in encryption (ChaCha20) with key rotation
- Real-time preview of resolved configs
- Event hooks (HTTP webhooks, Kafka, Redis Pub/Sub, NATS)
- Raw file serving (local, S3, FTP, SFTP, WebDAV, Vercel Blob)
- Inline editor with syntax validation (JSON, YAML, TOML)
Applications fetch resolved configuration from the /data/* endpoint using a token:
GET /data/<path>
Pass the token via the Authorization header:
curl -H "Authorization: Bearer pika_abc123..." http://localhost:8080/data/myapp/config| Parameter | Description | Example |
|---|---|---|
version |
Version selector — integer or semver | ?version=3 or ?version=0.2.0 |
variant |
Variant name | ?variant=prod |
format |
Convert output to a different format | ?format=json |
The response is returned in the file's stored format by default. Use ?format= to convert on the fly:
# Stored as YAML, returned as JSON
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?format=json| Format | Content-Type |
|---|---|
json |
application/json |
yaml |
application/x-yaml |
toml |
application/toml |
| other | application/octet-stream |
By default, /data/* requires a Bearer token. If you want to serve configs without authentication (e.g., inside a private network), enable the public server via the Settings > Public Server page in the UI.
The public port starts a second HTTP server that only exposes /data/*, /raw/*, and /healthz — no admin API, no UI. You can also enable Consul KV compatibility endpoints on it.
# No token needed on the public port
curl http://localhost:9090/data/myapp/config
# With variant and format
curl "http://localhost:9090/data/myapp/config?variant=prod&format=json"Every save creates a new version. Versions can be fetched by integer number or by semver.
Each save increments the version number (1, 2, 3, ...). Fetch a specific version:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?version=3Omit version to get the latest.
Versions can be tagged with a semver constraint (e.g., >= 0.1.0). When a consumer requests a semver version, pika resolves which version to serve based on constraint boundaries.
When you save a version with a constraint like >= 0.1.0, you're saying "this version is intended for consumers running 0.1.0 or higher." Pika walks the version history and finds the latest version whose constraint is satisfied by the requested semver.
Suppose a config has this version history:
| Version | Constraint | Content |
|---|---|---|
| v1 | (none) | Initial config |
| v2 | (none) | Minor tweak |
| v3 | >= 0.1.0 |
New field added in app 0.1.0 |
| v4 | (none) | Fix typo |
| v5 | >= 0.2.0 |
Breaking change for app 0.2.0 |
Consumers request their app version and get the right config:
# App running v0.0.5 — gets v2 (latest before the >= 0.1.0 boundary)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?version=0.0.5
# App running v0.1.5 — gets v4 (satisfies >= 0.1.0, latest before >= 0.2.0)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?version=0.1.5
# App running v0.2.0 — gets v5 (satisfies >= 0.2.0)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?version=0.2.0This lets you evolve configs alongside your application versions without breaking older deployments.
Variants are independent copies of a config for different environments or contexts (e.g., prod, staging, dev). Each variant has its own content, version history, and inheritance chain.
In the web UI, open a config file and use the Variants section in the right panel to add a variant (e.g., prod).
Variants are stored as path@variant internally (e.g., myapp/config@prod).
Use the variant query parameter:
# Base config
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config
# Production variant
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/data/myapp/config?variant=prod
# Staging variant, converted to JSON
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/data/myapp/config?variant=staging&format=json"
# Variant with semver version
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/data/myapp/config?variant=prod&version=0.3.0"Pika can serve files directly from local filesystem directories over HTTP. This is useful for serving static assets, certificates, or any files that don't need versioning or the config management features.
Raw mounts support multiple backend types. Mounts are configured via the Settings > Raw Mounts page in the UI.
Supported backend types:
- Local: Mount a filesystem directory (also works with FUSE mounts like
s3fs,rclone mount,sshfs,gcsfuse) - S3: AWS S3, MinIO, Cloudflare R2, DigitalOcean Spaces, or any S3-compatible storage
- FTP/FTPS: Connect to a remote FTP/FTPS server
- SFTP: Connect to a remote SFTP (SSH) server
- WebDAV: Connect to a WebDAV server
- Vercel Blob: Serve files from Vercel Blob storage
Files are served at /raw/{prefix}/{path}:
# Read a file (main server — requires Bearer token)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/raw/configs/app.json
# Read a file (public server — no auth)
curl http://localhost:9090/raw/configs/app.json
# Directory listing (returns JSON array)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/raw/configs/
# Upload a file (S3 mounts only — requires write scope)
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data-binary @app.json \
http://localhost:8080/raw/assets/app.json
# Delete a file (S3 mounts only — requires delete scope)
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/raw/assets/app.jsonDirectory listings return a JSON array of entries:
[
{ "name": "app.json", "is_dir": false, "size": 1234 },
{ "name": "subdir", "is_dir": true, "size": 0 }
]Token scopes match against raw/{prefix}/{path} — for example, a token with scope raw/** can access all raw mounts, or raw/configs/** for a specific mount. Write operations require write permission, delete requires delete.
When raw mounts are configured, a Files link appears in the navigation bar. The file browser provides:
- Tree navigation — mount points shown as top-level nodes with backend type badges (Local, S3, FTP), directories expand on click
- Smart file viewing — files open with the appropriate viewer based on their extension:
- Text/Code — syntax-highlighted read-only editor for known text formats (JSON, YAML, Go, Python, Markdown, etc.)
- Images — inline preview for PNG, JPG, SVG, WebP, etc.
- Video/Audio — native browser player with controls for MP4, WebM, MP3, WAV, etc.
- PDF — embedded PDF viewer
- Binary — a placeholder with an "Open Anyway" button that shows a hex dump viewer
- Tabs — multiple files can be open simultaneously with right-click context menu (Close, Close Others, Close All)
- File info panel — shows file metadata (name, mount, path, size, content type)
- Download button — always available in the toolbar to download any file
- Large file protection — text files over 5 MB are truncated with a warning; hex viewer limited to 10 MB
- Write operations (S3 mounts only):
- Upload — upload files via the tree (upload button on hover)
- Create folder — create new directories
- Delete — delete files (delete button on hover)
Raw mounts can also be managed from Settings > Raw Mounts in the web UI. The form supports all backend types with conditional fields. Changes take effect immediately — no server restart required.
Pika can push event notifications when files or configs change. Hooks are configured via Settings > Hooks in the UI.
| Event | Trigger |
|---|---|
file.created |
File uploaded to a raw mount |
file.updated |
File overwritten on a raw mount |
file.deleted |
File deleted from a raw mount |
file.renamed |
File renamed |
file.copied |
File copied |
dir.created |
Directory created |
config.created |
Config created |
config.updated |
Config updated |
config.deleted |
Config deleted |
* |
All events |
Each hook can send events to one or more targets:
- HTTP Webhook — POST/PUT to any URL with custom headers
- Kafka — Produce messages to Kafka topics (supports TLS, SASL/PLAIN, SASL/SCRAM)
- Redis Pub/Sub — Publish to Redis channels (standalone or cluster, with TLS + mTLS)
- NATS — Publish to NATS subjects (token or user/password auth)
Hooks can be filtered by:
- Mounts — restrict to specific raw mount prefixes
- Path pattern — glob pattern for matching file paths (e.g.,
*.pdf)
Targets support a Go text/template body template for customizing the event payload. Available fields: .Type, .Mount, .Path, .Size, .Protocol, .User, .Timestamp.
TLS certificate file paths in hook targets (Kafka, Redis) support references to files stored in Pika itself:
raw://mount/path— read from a raw mountconfig://key— read from the config store- Plain file paths are also supported
Pika is configured via environment variables (prefixed with PIKA_) or a config file.
| Variable | Default | Description |
|---|---|---|
PIKA_SERVER_HOST |
(all interfaces) | Bind address |
PIKA_SERVER_PORT |
8080 |
Listen port (admin UI + authenticated data) |
PIKA_SERVER_PUBLIC_PORT |
Public data port (unauthenticated /data/*) |
|
PIKA_SERVER_BASE_PATH |
/ |
Base URL path |
PIKA_STORAGE_PATH |
data/pika.db |
SQLite database path |
PIKA_SECRET_ENCRYPTION_KEY |
Encryption key — setting this enables encryption | |
PIKA_LOG_LEVEL |
info |
Log level |
Built-in session-based authentication is always enabled. On first launch, the UI shows a setup screen to create the initial admin account.
Session lifetime and cookie properties can be tuned via the config file or environment variables:
server:
auth:
session_ttl: 24h
cookie:
secure: true
same_site: laxPika supports forward authentication (e.g., with Turna, Authelia, Authentik). Forward-auth is configured and toggled at runtime from Settings > Forward Auth in the UI — no server restart required. The middleware is hot-swapped via an ada Slot.
When enabled, pika uses a session-first strategy:
- If the request has a valid local session cookie, the user is authenticated immediately (forward-auth is skipped).
- If no session cookie is present and forward-auth is enabled, pika delegates authentication to the configured external auth service.
- If neither succeeds, the request gets a 401.
This means local login always works — even when forward-auth is active. The /api/v1/info endpoint is always accessible so the SPA can boot and show the local login page regardless of forward-auth redirects.
Under forward auth, pika can translate external group names into its own capability keys. This is configured at runtime in Settings > Forward Auth > Permissions tab in the UI, and persisted in the database alongside other settings. The pika-native capability keys are:
| Key | Grants |
|---|---|
files.read |
View configurations, versions, variants, render, search, convert |
files.write |
Create, update, delete configurations and variants |
raw.read |
Browse and download raw mount contents |
raw.write |
Upload, delete, rename, copy, move raw mount contents |
settings.manage |
View and modify server settings, admin secret, backup/restore, key rotation |
tokens.manage |
Create, edit, revoke API access tokens |
users.manage |
Create, edit, delete, kick users (built-in auth only) |
permissions.manage |
Define permission bundles and assign them (built-in auth only) |
Example mapping: if your gateway emits X-Groups: pika-editor,auditors for a user, a mapping like
pika-editor → files.read, files.write, raw.read
auditors → files.read, raw.read, tokens.manage
grants that user the union of both sets. A user in multiple groups gets the union; unknown groups are ignored. Users not in the Superadmins allowlist and without any matching group are denied any restricted action (403).
When external permissions are disabled (the default), forward-auth users have no permission checks applied — pika trusts the gateway completely. Enable enforcement from the Permissions tab once the mapping is in place.
The groups header name and value separator are configurable. If your gateway uses X-Pika-Roles with | separators, set both fields in the Permissions form; pika also accepts repeated header lines as a single concatenated list.
Forward-auth users are identified purely by username — pika does not create rows in its users table for them. User and Permission bundle management in the UI remains available only under built-in auth.
Kustomize manifests are provided in ci/kubernetes/. They include a Deployment, Service, ConfigMap (with seed user and public port), PVC, ServiceAccount, and a Gateway API HTTPRoute for pika.example.com.
Apply directly from the repository:
kubectl apply -k https://github.com/rakunlabs/pika/ci/kubernetesPin to a specific version:
kubectl apply -k "https://github.com/rakunlabs/pika/ci/kubernetes?ref=v0.1.0"Create your own kustomization.yaml that references the upstream manifests and overrides what you need:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/rakunlabs/pika/ci/kubernetes?ref=main
images:
- name: ghcr.io/rakunlabs/pika
newTag: v0.1.0
patches:
- target:
kind: ConfigMap
name: pika
patch: |
- op: replace
path: /data/pika.yaml
value: |
server:
port: "8080"
auth:
session_ttl: 24h
cookie:
secure: true
same_site: lax
storage:
sqlite:
dsn: file:/data/pika.db?cache=shared
- target:
kind: HTTPRoute
name: pika
patch: |
- op: replace
path: /spec/hostnames/0
value: pika.mydomain.comThen apply:
kubectl apply -k .The public port (9090) is only exposed as a ClusterIP service — accessible within the cluster at pika.pika.svc.cluster.local:9090, not through the gateway.