Skip to content

C0DK/chorbar

Repository files navigation

Chorbar

Chore management system.

Run with NixOs as flake

In your NixOS flake:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    chorbar.url = "github:C0DK/chorbar";
  };

  outputs = { self, nixpkgs, chorbar, ... }: {
    nixosConfigurations.your-host = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./your-existing-config.nix
        chorbar.nixosModules.default
      ];
    };
  };
}

Then:

sudo nixos-rebuild switch --flake .#your-host
curl http://localhost:8080/

App-level secrets (Brevo, etc.)

Point chorbar.envFile at an env file (KEY=VALUE lines) to inject it into the chorbar-web container at boot; leave it null (the default) to skip:

chorbar.envFile = "/run/secrets/testmaxing.env";

The module is agnostic about how that file gets produced. Render it with whatever secret manager your host config uses — sops-nix sops.templates, agenix, a statusFile from another flake, etc. For a sops-nix example, see the secrets section under observability below.

What the module sets up

  • services.postgresqlchorbar database, chorbar-pod login role, TCP enabled on 127.0.0.1 with trust auth for local connections.
  • virtualisation.podman + an oci-containers.chorbar-web container running chorbar:main from the locally-built image tarball. Exposes port 8080 on the host.

The module does not open the firewall, configure SSH, set up networking, or define users — that's intentionally left to the host config that imports it.

Observability (optional)

chorbar.nixosModules.observability adds Grafana (port 3000), Loki (log storage), Grafana Alloy (ships container logs from journald to Loki), and Prometheus (scrapes the app's /metrics endpoint). It also creates a grafana PostgreSQL role with read-only (SELECT) access to the chorbar database.

Import it alongside the default module:

modules = [
  ./your-existing-config.nix
  chorbar.nixosModules.default
  chorbar.nixosModules.observability   # ← add this
];

Secrets via sops-nix (covers chorbar.envFile + Grafana)

sops-nix is pulled in transitively by nixosModules.observability (the default module is secret-manager-agnostic). The age key defaults to the host's SSH host key (/etc/ssh/ssh_host_ed25519_key, generated by NixOS on first boot) — no age-keygen step.

One-time bootstrap (from your laptop, after first deploy):

  1. Grab the host's age public key from its SSH host key:

    ssh root@your-host 'cat /etc/ssh/ssh_host_ed25519_key.pub' \
      | nix run nixpkgs#ssh-to-age
    # → age1xyz...
  2. Create secrets.yaml in your config repo, encrypted to that key:

    GRAFANA_KEY=$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 48)
    nix shell nixpkgs#sops -c sops --age age1xyz... \
      --set '["grafana_secret_key"] "'"$GRAFANA_KEY"'"' secrets.yaml
    nix shell nixpkgs#sops -c sops secrets.yaml   # opens editor for more keys

    Add any keys you want in your chorbar env file (e.g. brevo_api_key) in the same file.

  3. Point sops-nix at the file in your host config:

    sops.defaultSopsFile = ./secrets.yaml;
    # No sops.age.keyFile needed — SSH host key is used by default.
  4. Commit secrets.yaml (the encrypted blob) to your config repo and redeploy. From here, every change = edit + re-encrypt + rebuild. No host-side manual step ever again.

At boot sops-nix decrypts each secret to /run/secrets/<name>. Grafana reads its key via $__file{...}. To feed the chorbar container, render an env file from sops (e.g. via sops.templates."testmaxing.env") and point chorbar.envFile at its .path. Rotate by editing secrets.yaml and redeploying.

What the observability module sets up

  • services.loki — log storage on 127.0.0.1:3100 (not public).
  • services.alloy — Grafana Alloy reads podman-chorbar-web.service journal entries, drops human-readable lines, parses Serilog compact-JSON, and extracts @l as a level label before shipping to Loki.
  • services.prometheus — scrapes 127.0.0.1:8080/metrics (the chorbar app) every 15s. Binds to 127.0.0.1:9090 only. The app emits chorbar_page_views_total{route,method,status} and chorbar_unique_visitors_total{route} (unique = sha256(ip+ua), 24h window), plus the standard prometheus-net HTTP/runtime metrics.
  • services.grafana — listens on :3000 with three pre-provisioned datasources: Loki (default), Prometheus, and Chorbar DB (read-only Postgres). Sign-up and anonymous access are disabled.
  • sops.secrets.grafana_secret_key — declared but not encrypted by chorbar; you supply the encrypted value (see above).
  • chorbar-grafana-db-grants — oneshot systemd service that grants CONNECT, USAGE, and SELECT on all current and future chorbar tables to the grafana role.

The module does not open the firewall. Add networking.firewall. allowedTCPPorts = [ 3000 ]; in your host config if you want Grafana reachable from outside. Loki, Prometheus, and Alloy bind to 127.0.0.1 only.

Note: the chorbar app's /metrics endpoint is served on the same port as the rest of the app (8080). If you put chorbar behind a public reverse proxy, block /metrics there — Prometheus scrapes over loopback and does not need it exposed publicly.

Blocking /metrics in Caddy

If you front chorbar with Caddy, return 404 for /metrics before reverse-proxying the rest. Example Caddyfile:

chorbar.example.com {
    @metrics path /metrics /metrics/*
    respond @metrics 404

    reverse_proxy 127.0.0.1:8080
}

The matcher fires before reverse_proxy, so the request never reaches the app from the public side. Local Prometheus keeps scraping 127.0.0.1:8080/metrics directly, bypassing Caddy entirely.

Hardening checklist

  • Change Grafana admin password on first login (/profile).
  • Put Grafana behind a TLS reverse proxy (nginx, Caddy) and then set services.grafana.settings.security.cookie_secure = true.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors