@quantize/cellar-server
v0.1.4
Published
[](https://gitlab.com/quantize-bg/cellar/-/blob/main/LICENSE)
Readme
@quantize/cellar-server
Self-hosted secrets manager. Encrypts secrets with AES-256-GCM, stores them in a single SQLite file, and serves them over a REST API. Runs on ~15 MB of RAM.
Quick Start
Via CLI (recommended)
npm i -g @quantize/cellar-cli
cellar server start # pulls Docker image & starts container
cellar login http://localhost:3000 # auto-detects fresh server, runs setupDocker (manual)
docker run -d -p 3000:3000 -v cellar-data:/data registry.gitlab.com/quantize-bg/cellarFrom Source
git clone https://gitlab.com/quantize-bg/cellar.git
cd cellar && pnpm install
pnpm dev # starts with hot reload on :3000First-time Setup
If not using the CLI, the first request to /api/v1/setup initializes the database:
curl -X POST http://localhost:3000/api/v1/setup \
-H 'Content-Type: application/json' \
-d '{"name": "admin"}'Save the returned API key — it is shown only once.
API Reference
All endpoints require Authorization: Bearer <api-key> except health and setup.
Health & Setup
GET /api/v1/health # { status: "ok", initialized: true }
POST /api/v1/setup # { name: "admin" } → admin user + API keyProjects
GET /api/v1/projects # list all
POST /api/v1/projects # { name: "my-app" }
GET /api/v1/projects/:id # get one
DELETE /api/v1/projects/:id # deleteEnvironments
GET /api/v1/projects/:id/envs # list
POST /api/v1/projects/:id/envs # { name: "Production", slug: "production" }
DELETE /api/v1/projects/:id/envs/:slug # deleteSecrets
GET /api/v1/projects/:id/envs/:slug/secrets # list (?format=env|json)
GET /api/v1/projects/:id/envs/:slug/secrets/:key # get one
PUT /api/v1/projects/:id/envs/:slug/secrets/:key # upsert { value: "..." }
DELETE /api/v1/projects/:id/envs/:slug/secrets/:key # delete
POST /api/v1/projects/:id/envs/:slug/secrets/bulk # { secrets: [{ key, value }] }Users
GET /api/v1/users # list
POST /api/v1/users # { name: "CI Bot", role: "member" }
DELETE /api/v1/users/:id # deleteAPI Keys
GET /api/v1/users/:id/api-keys # list
POST /api/v1/users/:id/api-keys # { name: "deploy" }
DELETE /api/v1/users/:id/api-keys/:keyId # revokeAccess Control
GET /api/v1/projects/:id/access/:userId # list
PUT /api/v1/projects/:id/access/:userId # { permission: "read" | "write" }
DELETE /api/v1/projects/:id/access/:userId # revokeSecurity
| Layer | Implementation |
|-------|----------------|
| Encryption | AES-256-GCM with unique 12-byte IV per secret |
| Master key | 256-bit, stored at data/cellar.key (0600) or CELLAR_MASTER_KEY env var |
| API keys | SHA-256 hashed before storage; raw key shown only at creation |
| Database | SQLite with WAL mode + foreign key constraints |
Configuration
| Env var | Default | Description |
|---------|---------|-------------|
| PORT | 3000 | Server port |
| HOST | 0.0.0.0 | Bind address |
| DATA_DIR | ./data | Data directory (SQLite file + master key) |
| CELLAR_MASTER_KEY | auto-generated | Base64-encoded 256-bit key |
Stack
- Runtime: Bun with built-in SQLite
- Framework: Hono (~14kb)
- Database: SQLite (6 tables: users, api_keys, projects, project_access, environments, secrets)
- Encryption: Node.js
cryptomodule (AES-256-GCM)
Database Schema
Six tables, all SQLite with WAL mode and foreign keys enabled.
| Table | Columns | References |
|-------|---------|------------|
| users | id, name, role, created_at | — |
| api_keys | id, user_id, key_hash, key_prefix, name, created_at, last_used_at | user_id → users |
| projects | id, name, created_at | — |
| project_access | user_id, project_id, permission | user_id → users, project_id → projects |
| environments | id, project_id, name, slug, created_at | project_id → projects |
| secrets | id, environment_id, key, encrypted_value, iv, created_at, updated_at | environment_id → environments |
All foreign keys cascade on delete.
Part of the Cellar Monorepo
| Package | Description | |---------|-------------| | @quantize/cellar-server | This package — Hono + SQLite server | | @quantize/cellar-cli | Interactive + scriptable CLI (npm) |
License
MIT
