Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
04f0b12
feat: initial port of Nix packaging from happier monorepo
das-monki Feb 24, 2026
c10bee4
refactor: flatten nix/ into top-level directories
das-monki Feb 24, 2026
fe98f11
refactor: idiomatic layout — modules/nixos/ for exported modules
das-monki Feb 24, 2026
0dae6dc
fix: CI failures — treefmt flag and autoPatchelfHook on darwin
das-monki Feb 25, 2026
1e9fb98
style: apply nixfmt to prisma-engines-prebuilt.nix
das-monki Feb 25, 2026
6cb0ec5
feat: add flake checks, CI improvements, and automated input updates
das-monki Feb 26, 2026
7e4bcb1
refactor: drop update workflows and tag pinning for now
das-monki Feb 26, 2026
8911b06
feat(nix): replace update-prisma-hashes script with flake apps
das-monki Feb 26, 2026
3431d43
refactor(nix): extract light server example from checks.nix
das-monki Feb 26, 2026
b564a6b
docs: add README and trim devshell to nix-happier scope
das-monki Feb 26, 2026
e59ca9b
docs: add pre-release notice about branch tracking and manual updates
das-monki Feb 26, 2026
a1c0f93
docs: add AGENTS.md with Nix-specific guidelines and symlink CLAUDE.md
das-monki Feb 26, 2026
8c3f982
docs: add Tailscale example and clarify environmentFile requirement
das-monki Feb 26, 2026
e10ede5
refactor(examples): replace nginx with Caddy for Tailscale TLS
das-monki Feb 26, 2026
df299d7
docs: add backup responsibility note for full mode
das-monki Feb 26, 2026
94a05c8
ci: switch aarch64-linux to native ARM runner
das-monki Feb 27, 2026
385000c
docs(examples): add full mode example with Tailscale + Caddy
das-monki Feb 27, 2026
be7a27b
feat: build and bundle web UI with happier-server
das-monki Feb 27, 2026
dcf0f17
chore: switch happier input to preview branch
das-monki Mar 2, 2026
111c3ad
track happier dev branch, update flake inputs
das-monki Apr 8, 2026
3941742
fix: add new workspace packages for happier dev branch
das-monki Apr 8, 2026
77be10a
bump happier input
das-monki Apr 18, 2026
6415421
bump happier input
das-monki Apr 21, 2026
3e03c2e
feat: allow overriding default server URL for happier-cli
das-monki Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/nix-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI — Nix Builds

on:
pull_request:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
nix-build-x86_64-linux:
name: Nix Build (x86_64-linux)
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/nix-build.yml

Repository: happier-dev/nix-happier

Length of output: 2212


Pin workflow actions to immutable commit SHAs instead of mutable refs.

Using version tags (@v4) or branch refs (@main) allows upstream changes to alter CI behavior without review. Pin each uses: to a commit SHA for reproducibility and security.

This workflow uses:

  • actions/checkout@v4 (lines 20, 37, 51) — version tag is mutable
  • DeterminateSystems/nix-installer-action@main (lines 23, 40, 54) — branch ref is explicitly mutable
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/nix-build.yml at line 20, Update mutable action refs to
immutable commit SHAs: replace uses: actions/checkout@v4 with uses:
actions/checkout@<commit-sha> and replace
DeterminateSystems/nix-installer-action@main with
DeterminateSystems/nix-installer-action@<commit-sha>; do this for every
occurrence of those two action refs in the workflow file. Locate the exact
commit SHAs from each action's upstream GitHub repo (releases/tags or the commit
you want to pin), then substitute the tag/branch with that SHA to ensure
reproducible CI.


- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

- name: Nix flake check
run: nix flake check

- name: Check formatting
run: nix fmt -- --fail-on-change

nix-build-aarch64-darwin:
name: Nix Build (aarch64-darwin)
runs-on: macos-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

- name: Nix flake check
run: nix flake check

nix-build-aarch64-linux:
name: Nix Build (aarch64-linux)
runs-on: ubuntu-24.04-arm
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

# KVM is not available on GitHub ARM runners, so the NixOS VM
# integration test cannot run here. Build packages and run linters
# only; the full VM test runs in the x86_64-linux job.
- name: Build packages
run: |
nix build .#packages.aarch64-linux.happier-server
nix build .#packages.aarch64-linux.happier-cli

- name: Lint
run: |
nix build .#checks.aarch64-linux.deadnix
nix build .#checks.aarch64-linux.statix
88 changes: 88 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# nix-happier — Agent Guidelines

This file provides guidance for AI coding agents (Claude Code, Copilot, Codex, etc.) working in this repo.

## What this repo is

A Nix flake that builds and deploys [Happier](https://github.com/happier-dev/happier) Server and CLI. It does **not** contain application source code — the happier monorepo is fetched as a flake input. This repo only has Nix expressions, NixOS modules, and supporting config.

## Repo layout

```
flake.nix # Flake entrypoint (inputs, systems, imports)
packages.nix # happier-server + happier-cli derivations
checks.nix # deadnix, statix, NixOS VM integration test
devshell.nix # Dev shell (fmt, update commands)
modules/nixos/happier-server.nix # NixOS service module
packages/prisma-engines-prebuilt.nix # Prebuilt Prisma engine binaries
examples/happier-server-tailscale.nix # Recommended production setup (Tailscale + Caddy)
examples/happier-server-light.nix # Minimal config (used by CI VM test)
```
Comment on lines +11 to +20
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language to the fenced code block.

The repo layout block should specify a language (e.g., text) to satisfy markdownlint MD040.

Proposed fix
-```
+```text
 flake.nix                        # Flake entrypoint (inputs, systems, imports)
 packages.nix                     # happier-server + happier-cli derivations
 checks.nix                       # deadnix, statix, NixOS VM integration test
 devshell.nix                     # Dev shell (fmt, update commands)
 modules/nixos/happier-server.nix # NixOS service module
 packages/prisma-engines-prebuilt.nix # Prebuilt Prisma engine binaries
 examples/happier-server-tailscale.nix # Recommended production setup (Tailscale + Caddy)
 examples/happier-server-light.nix    # Minimal config (used by CI VM test)
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 11-11: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 11 - 20, The fenced code block in AGENTS.md (the repo
layout block listing flake.nix, packages.nix, etc.) is missing a language tag;
update the opening fence from ``` to ```text so the block becomes a ```text
fenced code block to satisfy markdownlint MD040.


## Language and tooling

- **All code is Nix.** There is no TypeScript, Python, or other source code here.
- **Formatter**: `nixfmt-tree` (the flake's `formatter`). Run `nix fmt` to format.
- **Linters**: `deadnix` (unused bindings) and `statix` (anti-patterns). Both run via `nix flake check`.
- **statix config**: `statix.toml` disables `repeated_keys` (W20) — NixOS modules idiomatically use separate top-level assignments with `lib.mkIf` guards.

## Nix style conventions

- Use `nixfmt-tree` style (the RFC-style formatter). Do not manually reformat — run `nix fmt`.
- Prefer `lib.mkIf` / `lib.mkOption` / `lib.optional` over raw `if-then-else` in NixOS modules.
- Use `let ... in` for local bindings. Keep `let` blocks close to where they're used.
- Avoid `with pkgs;` at module scope — use it only in narrow scopes (e.g. inside `buildInputs` lists).
- `flake-parts` is used for per-system logic. New per-system outputs go in dedicated `.nix` files imported by `flake.nix`.
- Comments should explain *why*, not *what*. Nix expressions are usually self-documenting.

## Key patterns

### Flake structure

The flake uses [flake-parts](https://github.com/hercules-ci/flake-parts). Per-system outputs (packages, checks, devshell, apps) are split into separate files and imported in `flake.nix`. Flake-level outputs (`nixosModules`) are defined in the `flake = { ... }` attrset.

### NixOS module

`modules/nixos/happier-server.nix` defines `services.happier-server` options. It supports two modes:
- **full**: PostgreSQL + Redis + MinIO (production stack)
- **light**: SQLite-only, no external deps

The module provisions supporting services (PostgreSQL, Redis, MinIO) when `createLocally = true` and handles migrations, WAL mode, and secret loading via `systemd` `LoadCredential`.

Both modes require an `environmentFile` containing `HANDY_MASTER_SECRET` for production use. The only exception is the CI VM test, which omits it.

### Package builds

Both `happier-server` and `happier-cli` are built from the happier monorepo source (`inputs.happier`). Prisma engines come from `packages/prisma-engines-prebuilt.nix` which fetches prebuilt binaries by hash.

### Updating dependencies

When the happier monorepo updates `@prisma/client`, the engine hashes here must also be updated:

```sh
nix run .#update # updates flake inputs + Prisma hashes
nix run .#update-prisma-hashes # updates only Prisma hashes
```

## Verification

Always verify changes with:

```sh
nix fmt # format
nix flake check # lint (deadnix, statix) + VM integration test (Linux)
nix build .#happier-server # build the server package
```

`nix flake check` runs the NixOS VM integration test on Linux — it boots a VM with the light-mode example and verifies the server starts and responds on port 3005.

## Do's and don'ts

- **Do** run `nix fmt` after every change.
- **Do** run `nix flake check` before considering work done (or at minimum `nix build`).
- **Do** keep the NixOS module options in sync with the README's "Module options" table.
- **Do** add examples to `examples/` for new configurations and reference them in checks if testable.
- **Don't** modify `flake.lock` by hand — use `nix flake update` or `nix run .#update`.
- **Don't** add packages, commands, or dependencies that belong in the happier monorepo, not here.
- **Don't** introduce `with pkgs;` at module or file scope.
- **Don't** create migrations or modify Prisma schemas — that happens in the happier monorepo.
1 change: 1 addition & 0 deletions CLAUDE.md
194 changes: 194 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,195 @@
# nix-happier

Nix flake for building and deploying [Happier](https://github.com/happier-dev/happier) Server and CLI.

> **Pre-release notice:** The `happier` flake input currently tracks the `main` branch. This will be pinned to tagged releases once Happier reaches a stable version. Updates to this flake are made manually for now — run `nix run .#update` to pull the latest.

## Flake outputs

| Output | Description |
|--------|-------------|
| `packages.happier-server` | Happier Server (full + light mode binaries) |
| `packages.happier-cli` | Happier CLI |
| `nixosModules.happier-server` | NixOS module for running Happier Server as a systemd service |
| `checks.deadnix` | Unused binding detection |
| `checks.statix` | Nix anti-pattern linting |
| `checks.nixos-happier-server-light` | NixOS VM integration test (Linux only) |
| `apps.update-prisma-hashes` | Update Prisma engine binary hashes |
| `apps.update` | Update all flake inputs + Prisma hashes |
| `devShells.default` | Dev shell with git and nixfmt |

Supported systems: `aarch64-darwin`, `aarch64-linux`, `x86_64-linux`

## Secrets

All deployment modes require an **environment file** containing secrets. At minimum:

```sh
HANDY_MASTER_SECRET=<your-secret>
```

Pass it to the module via `environmentFile`. Use [agenix](https://github.com/ryantm/agenix), [sops-nix](https://github.com/Mic92/sops-nix), or a plain file with restricted permissions — whatever fits your secrets workflow.

## Quick start

Add the flake input and import the NixOS module:

```nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nix-happier.url = "github:happier-dev/nix-happier";
};

outputs = { nixpkgs, nix-happier, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
nix-happier.nixosModules.happier-server
{
services.happier-server = {
enable = true;
package = nix-happier.packages.x86_64-linux.happier-server;
mode = "light";
environmentFile = "/run/secrets/happier-env";
};
}
];
};
};
}
```

### Recommended: Tailscale + Caddy

The most common setup serves Happier over your Tailscale network with automatic TLS via Caddy. See [`examples/happier-server-tailscale.nix`](examples/happier-server-tailscale.nix) for a complete configuration that includes:

- Happier Server in light mode on `localhost:3005`
- Tailscale for private networking
- Caddy reverse proxy with automatic TLS cert provisioning

### Light mode (minimal)

See [`examples/happier-server-light.nix`](examples/happier-server-light.nix) for the bare minimum. This is what the CI integration test uses — it omits `environmentFile` and networking, so it's useful as a starting point but not production-ready on its own.

### Full mode (PostgreSQL + Redis + MinIO)

With `createLocally = true` (the default), PostgreSQL, Redis, and MinIO are all provisioned on the same host as single-node instances. This is convenient for small deployments but comes with no built-in replication or backups — **you are responsible for setting up your own backup strategy** (e.g. `pgBackRest`, `restic`, or filesystem snapshots).

```nix
{
services.happier-server = {
enable = true;
package = nix-happier.packages.x86_64-linux.happier-server;
mode = "full";
port = 3005;
environmentFile = "/run/secrets/happier-env";

database = {
name = "happier";
user = "happier";
createLocally = true; # provisions PostgreSQL 15
};

redis.createLocally = true;

minio = {
createLocally = true;
bucket = "happier";
rootCredentialsFile = "/run/secrets/minio-creds"; # MINIO_ROOT_USER + MINIO_ROOT_PASSWORD
};
};
}
```

## Module options

### Core

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enable` | bool | `false` | Enable Happier Server |
| `package` | package | — | The `happier-server` package to use |
| `port` | port | `3005` | Port to listen on |
| `mode` | `"full"` \| `"light"` | `"full"` | `full` = PostgreSQL + Redis + MinIO; `light` = SQLite only |
| `environmentFile` | path \| null | `null` | **Required for production.** Secrets file (`KEY=value`) — must contain `HANDY_MASTER_SECRET` |

### Database (full mode)

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `database.name` | string | `"happier"` | PostgreSQL database name |
| `database.user` | string | `"happier"` | PostgreSQL user |
| `database.createLocally` | bool | `true` | Provision PostgreSQL locally |

### Redis (full mode)

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `redis.createLocally` | bool | `true` | Provision Redis locally |

### MinIO (full mode)

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `minio.createLocally` | bool | `true` | Provision MinIO locally for S3-compatible storage |
| `minio.bucket` | string | `"happier"` | MinIO bucket name |
| `minio.rootCredentialsFile` | path \| null | `null` | File with `MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD` |

## Examples

The [`examples/`](examples/) directory contains NixOS configurations:

| Example | Description |
|---------|-------------|
| [`happier-server-tailscale.nix`](examples/happier-server-tailscale.nix) | Recommended production setup — light mode + Tailscale + Caddy TLS |
| [`happier-server-light.nix`](examples/happier-server-light.nix) | Bare minimum for CI — tested via `nix flake check` VM integration test |

## Development

Enter the dev shell:

```sh
nix develop
```

Available commands inside the shell:

| Command | Description |
|---------|-------------|
| `fmt` | Format Nix files |
| `update` | Update flake inputs + Prisma hashes |

Run linting and integration tests:

```sh
nix flake check
```

Update all inputs and refresh Prisma engine hashes:

```sh
nix run .#update
```

## Repo structure

```
.
├── flake.nix # Flake entrypoint
├── flake.lock
├── packages.nix # happier-server + happier-cli derivations
├── checks.nix # deadnix, statix, NixOS VM test
├── devshell.nix # Dev shell with commands
├── modules/
│ └── nixos/
│ └── happier-server.nix # NixOS module
├── packages/
│ └── prisma-engines-prebuilt.nix # Prebuilt Prisma engine binaries
├── examples/
│ ├── happier-server-tailscale.nix # Production setup with Tailscale + Caddy
│ └── happier-server-light.nix # Minimal config (used by CI)
└── .github/
└── workflows/
└── nix-build.yml # CI workflow
```
Comment on lines +177 to +195
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Specify a language for the repo-structure code fence.

This block should declare a language (text) to pass markdownlint MD040.

Proposed fix
-```
+```text
 .
 ├── flake.nix                          # Flake entrypoint
 ├── flake.lock
 ├── packages.nix                       # happier-server + happier-cli derivations
 ├── checks.nix                         # deadnix, statix, NixOS VM test
 ├── devshell.nix                       # Dev shell with commands
 ├── modules/
 │   └── nixos/
 │       └── happier-server.nix         # NixOS module
 ├── packages/
 │   └── prisma-engines-prebuilt.nix    # Prebuilt Prisma engine binaries
 ├── examples/
 │   ├── happier-server-tailscale.nix   # Production setup with Tailscale + Caddy
 │   └── happier-server-light.nix       # Minimal config (used by CI)
 └── .github/
     └── workflows/
         └── nix-build.yml              # CI workflow
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

[warning] 177-177: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @README.md around lines 177 - 195, The fenced code block showing the repo
tree in README.md is missing a language specifier; update the opening fence from
totext for the repository-structure block so markdownlint MD040 passes
(i.e., change the block that starts with the tree listing to begin with text and close with ). Ensure only that code fence is modified and the tree
contents (the lines with flake.nix, packages.nix, modules/, examples/, .github/,
etc.) remain unchanged.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

Loading