Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@../AGENTS.md
92 changes: 92 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# AGENTS.md

Guidance for AI coding agents working in this repository.

## Overview

Smallstep's Nix User Repository (NUR): Nix packaging for `step-agent` plus the `services.step-agent` NixOS module. It is consumed as a flake (`github:smallstep/nur`), as a classic NUR package set (`import ./default.nix { inherit pkgs; }`), or as a nixpkgs overlay (`overlay.nix`). There is no source code to compile here: every package fetches a prebuilt Linux tarball (`x86_64-linux` and `aarch64-linux` only) and runs it through `autoPatchelfHook`. The only flake input is `nixpkgs` (`nixpkgs-unstable`). This is a public, MIT-licensed repository.

## Commands

Nix with `nix-command` and `flakes` enabled is required. Packages only build on Linux; on macOS you can evaluate but not build.

```bash
nix flake show # list outputs: packages, legacyPackages, nixosModules
nix flake check # evaluate every output against flake.lock
nix build .#step-agent # newest stable release
nix build .#step-agent_0_70_0-rc2 # a specific release; prereleases only by explicit attribute
nix-build -A step-agent # classic path, uses <nixpkgs> from NIX_PATH, not flake.lock
nix flake update # bump flake.lock (manual, see "CI" below)
```

What CI actually runs (`.github/workflows/build.yml`), reproducible locally with `<nixpkgs>` on `NIX_PATH`:

```bash
# evaluation check
nix-env -f . -qa \* --meta --xml --drv-path --show-trace \
--option restrict-eval true --option allow-import-from-derivation true \
-I "nixpkgs=$(nix-instantiate --find-file nixpkgs)" -I "$PWD"
# build every cacheable derivation
nix shell -f '<nixpkgs>' nix-build-uncached -c nix-build-uncached ci.nix -A cacheOutputs
```

There is no Makefile, test suite, linter, or formatter. Build products land in gitignored `result` / `result-*` symlinks.

## Generated code — do not edit

| Pattern | Generator |
|---------|-----------|
| `pkgs/step-agent/step-agent_<version>.nix` | GoReleaser, in the agent's release pipeline (each file is headed `This file was generated by GoReleaser. DO NOT EDIT.`) |
| `nixos-modules/step-agent.nix` | Mirrored on merge from the upstream agent repository (`extra/step-agent.nix`, linked from README.md); send changes there |

The `step-ci` bot commits both directly to `main`, not through pull requests. Hand edits are overwritten by the next release or mirror.

## Architecture

```
nur/
├── flake.nix # packages + legacyPackages per system; nixosModules (not per system)
├── flake.lock # pins nixpkgs for flake users; NOT exercised by CI
├── default.nix # NUR entry point: derives the package set from pkgs/ at eval time
├── ci.nix # filters default.nix into buildPkgs / cachePkgs / cacheOutputs
├── overlay.nix # nixpkgs overlay exposing the same attributes
├── lib/, overlays/ # NUR-template placeholders, empty
├── nixos-modules/
│ ├── default.nix # { step-agent = ./step-agent.nix; }
│ └── step-agent.nix # services.step-agent: user, systemd units, PKCS#11 socket, polkit
└── pkgs/step-agent/ # one derivation per release, ~70 files, oldest 0.65.0-rc11
```

### How the package list is generated

There is no checked-in package list. `default.nix` calls `builtins.readDir` on `pkgs/<name>/` at evaluation time and registers every `<name>_<version>.nix` as attribute `<name>_<version>` with `.` replaced by `_` (`step-agent_0.69.2.nix` becomes `step-agent_0_69_2`; a `-rc1` suffix is kept verbatim). The bare `<name>` attribute is the newest version matching `[0-9]+\.[0-9]+\.[0-9]+`, compared with `builtins.compareVersions`, so a prerelease is never the default. `flake.nix` exposes the whole set as `legacyPackages` and the derivations alone as `packages`. Adding a second package family is one more `packageSet "<name>"` line at the bottom of `default.nix`.

### How a new version lands

1. The agent's release pipeline runs GoReleaser, which renders `pkgs/step-agent/step-agent_<version>.nix` (download URL and sha256 per platform) and pushes it straight to `main` as `step-ci`, message `step-agent: vOLD -> vNEW`.
2. That push triggers `build.yml`, which evaluates and builds every cacheable derivation on three nixpkgs channels (`nixpkgs-unstable`, `nixos-unstable`, `nixos-25.11`) with `nix-build-uncached`.
3. Nothing else changes: no list to update, no lock to bump. Old versions are never removed automatically.

Because `ci.nix` builds everything under `pkgs/`, one release whose tarball has gone missing breaks CI for the whole repo; deleting that file is the fix. Derivations are not uniform across time (older files use `pname = "step-agent-plugin"` and GitHub release URLs; newer ones use `pname = "step-agent"` and `packages.smallstep.com`), because each reflects the GoReleaser config of its day.

### NixOS module

`nixosModules.step-agent` exposes `services.step-agent.{enable,package,settings}`. `package` defaults to `pkgs.step-agent` from the host's nixpkgs channel, not this repository's build; README.md shows how to point it here. `settings` is rendered to `/etc/step-agent/agent.yaml` when set, otherwise `step-agent register` writes that file. The module is deliberately a single self-contained file: it is also published standalone at `https://files.smallstep.com/step-agent.nix`, and its option surface tracks the module proposed for nixpkgs in NixOS/nixpkgs#555971. It requires a hardware TPM 2.0.

## CI and workflows

| Workflow | Trigger | What it does |
|----------|---------|--------------|
| `build.yml` | PR, push to `main`, manual | eval check + `nix-build-uncached ci.nix -A cacheOutputs` on 3 nixpkgs channels |
| `actionci.yml` | PR (its push trigger names `master`, which does not exist) | actionlint + zizmor via `smallstep/workflows`; policy in `.github/zizmor.yml` |
| `dependabot-auto-merge.yml` | PR | auto-merges Dependabot; only the `github-actions` ecosystem is configured, weekly, 7-day cooldown |
| `test-commit-push.yml`, `test-pat-push.yml` | manual only | smoke tests for the bot's SSH signing key and push token; not part of CI |

`build.yml` selects nixpkgs through `NIX_PATH` and `nix-env -f .`, so `flake.lock` is never evaluated in CI. `nix flake check` and `nix build` locally do use it, which means a stale lock can break flake consumers while CI stays green. Update it by hand with `nix flake update` in its own PR.

## Conventions

- Version bumps and module mirrors are bot commits to `main`. Human PRs are for `default.nix`, `flake.nix`, `ci.nix`, CI workflows, and README.md.
- Third-party actions are SHA-pinned; `smallstep/*` reusable workflows may use `@main` (zizmor `unpinned-uses` policy).
- Every package is `stdenvNoCC.mkDerivation` + `autoPatchelfHook`, with `meta.sourceProvenance = binaryNativeCode` and `meta.platforms` limited to the two Linux targets; the binary is wrapped with `tpm2-tss`, `tpm2-openssl`, `desktop-file-utils`, `polkit`, and `p11-kit` on `PATH`.
- `flake.nix` sets no `nixConfig` and no binary cache; consumers build from source or hit their own cache.