# CodeVault A small HTTP service that stores string values under access codes: write a value with `setValue` under a code, later retrieve it with `getValue` using that code. Basic Auth protects writes; reads are open but rate-limited by failed-lookup count per client IP to slow down code guessing. ## Features - `POST /api/v1/setValue` — store a value under a code (Basic Auth required). - `POST /api/v1/getValue` — retrieve a value by code (public, rate-limited). - Interactive API docs at `/api` (Swagger UI, embedded assets — no CDN). - Prometheus metrics for HTTP requests and business events (successful/failed gets and sets). - OpenTelemetry logging, with optional OTLP export. - SQLite storage (pure Go driver, no CGO). - Configurable trusted proxies so client IPs are resolved correctly behind a reverse proxy (e.g. nginx). ## Running ### Locally ```sh go build ./codevault config-example ./config.yaml # write a starter config ./codevault add-user --db-path ./codevault.db --login admin # prompts for a password ./codevault run --config ./config.yaml ``` Or via the Makefile: `make build`, `make config-example`, `make add-user`, `make run`. ### Docker ```sh cp docker-compose.yaml.example docker-compose.yaml go run . config-example ./config.yaml # or hand-write one, see Configuration below docker compose up -d --build docker compose exec codevault ./codevault add-user --db-path /app/data/codevault.db --login admin ``` The compose file mounts `config.yaml` read-only and keeps the SQLite database in a named volume (`codevault-data`). Edit `server.trusted_proxies` in your `config.yaml` to match the proxy/network actually in front of the container (gin needs IPs/CIDRs, not hostnames). ## Configuration `config-example` writes a documented starting point. Key sections: ```yaml environment: development # development | production server: address: :8080 trusted_proxies: # IPs/CIDRs allowed to set X-Forwarded-For / X-Real-IP - 172.16.0.0/12 # empty/omitted = trust nothing, use the raw remote address database: path: ./codevault.db rate_limit: max_failures: 10 # failed getValue lookups per IP before 404-ing everything window: 1m0s # sliding window for max_failures observability: logging: stderr: INFO # DEBUG|INFO|WARNING|ERROR|PANIC|FATAL, empty disables files: INFO: ./info.log # per-level log files, optional exporter: # optional OTLP log export level: INFO endpoint: http://127.0.0.1:4317/v1/logs transport: grpc # grpc | http metrics: host: 127.0.0.1 port: 9092 # Prometheus /metrics endpoint ``` ## API Full OpenAPI spec: [internal/api/openapi.yaml](internal/api/openapi.yaml). When the server is running, browse it interactively at `/api`, or fetch the raw spec from `/api/openapi.json`. - `POST /api/v1/getValue` `{"code": "..."}` → `200 {"value": "..."}` / `404` (not found or rate-limited) / `400` (bad body). - `POST /api/v1/setValue` `{"code": "...", "value": "..."}` (Basic Auth) → `204` / `400` (bad body) / `401` (bad credentials). ## Metrics Exposed on `http://:/metrics`: - `http_requests_total`, `http_request_duration_seconds` — generic per-route HTTP metrics. - `codevault_get_value_success_total{code}` — successful getValue lookups, by code. - `codevault_get_value_failed_total{reason}` — failed lookups, `reason` is `not_found` or `rate_limited`. - `codevault_set_value_success_total{user}` — successful setValue writes, by authenticated login. - `codevault_set_value_failed_total{reason}` — failed writes, `reason` is `bad_request` or `unauthorized`. ## Development ```sh make build # go build make lint # golangci-lint make generate # regenerate internal/api from openapi.yaml (oapi-codegen) make test # go test ./... make docker-build ```