Files in the top-level directory from the latest check-in
- .amber.yml
- .ameba.yml
- .dockerignore
- .gitignore
- .fossil-settings
- app
- config
- db
- docker
- public
- script
- spec
- src
- docker-compose.prod.yml
- docker-compose.yml
- Dockerfile
- README.md
- RELEASE_NOTES.md
- shard.lock
- shard.yml
Carnelian
Personal Fossil repository manager, documentation site, and blog publisher.
Stack: Crystal 1.21.0, Amber 2.0.0-beta.5, SQLite3.
Development
shards install
./script/ci
AMBER_ENV=development ./bin/carnelian
script/ci installs frozen shards, then runs format check, Ameba, specs, and a compile. Any step failure exits non-zero. Individual steps: ./script/ci format, ./script/ci ameba, ./script/ci spec, ./script/ci build.
Health check: GET /health/live
shards build carnelian
./bin/carnelian migrate
AMBER_ENV=development ./bin/carnelian web
Development uses config/environments/development.yml. Specs set AMBER_ENV=test. Production reads host, port, secret, and database URL from environment variables (AMBER_SERVER_*, DATABASE_URL).
docker compose up --build
Only the gateway service publishes a host port (8080). Web, worker, and fossil stay on the internal compose network. The current Fossil supervisor is a process placeholder until the dedicated supervisor task.
Master key (64 hex characters, 32 bytes) is loaded from CARNELIAN_MASTER_KEY, CARNELIAN_MASTER_KEY_FILE, or /run/secrets/carnelian_master_key. Credentials are stored with AES-256-GCM. Logs must pass through Carnelian::LogRedactor so passwords, tokens, and credential URLs are not written.
Application layout follows docs/architecture.md: controllers, views, pipes, models, repositories, services, jobs, integrations, infrastructure, and CLI.
Production Docker deployment
Build the image with the pinned Crystal/Amber inputs and explicit Fossil/Git
labels, then create four host-only secret files (mode 0600): a 64-character
hex master key, an Amber server secret, a one-time setup token, and an internal
health token. Copy docker/compose.prod.env.example to a private env file and
change the *_SOURCE paths. Start the production overlay with:
docker compose --env-file docker/compose.prod.env \
-f docker-compose.yml -f docker-compose.prod.yml up -d
The web container runs carnelian migrate before serving traffic; the worker
waits for the web health check. The named carnelian-data volume contains the
SQLite database, Fossil repositories, Git mirrors, rendered assets, uploads,
and SSH material. Do not remove it during upgrades. Pull a new image, run the
same command to recreate services, and keep the master key unchanged so stored
credentials remain decryptable. The container creates the volume directories
and assigns them to UID/GID 1001 (carnelian); a pre-existing bind mount must
be writable by that user.
On first start, open /setup and provide the setup token. Subsequent restarts
reuse the volume and skip initialization. To reset the administrator password,
run docker compose exec web carnelian admin reset-password; the command reads
the new password from the terminal and revokes existing sessions.
