## Summary Migrates the docs build pipeline to Dagger (Phase 2 of the Dagger CI adoption plan). - **Backfill `date-modified` frontmatter** on all 80 docs — Dagger's `--src=.` excludes `.git`, so Quartz can't use git history for page dates. Frontmatter dates work with or without git. - **New `docs-check-frontmatter` mise task + pre-commit hook** — validates all docs have `title`, `tags`, and `date-modified` - **New Dagger functions** — `build_changelog` (towncrier in Python container) and `build_docs` (chains changelog → Quartz build in Node container, returns tarball) - **Simplified CI workflow** — the ~44-line inline Quartz build (clone, npm ci, build, tar, cleanup) is replaced by `dagger call build-docs`. Changelog step remains local on the runner since towncrier needs to modify the host working tree for the git commit. ### Design decisions - **Towncrier runs twice in CI**: once inside Dagger (for the docs tarball) and once on the runner (for the git commit). This is intentional — Dagger's directory export is additive and can't delete the consumed changelog fragments from the host. - **Artifact hosting stays on Forgejo Releases** (not migrated to Forgejo Packages as the plan doc originally suggested). That migration can happen independently. - **`date-modified` frontmatter** preserved even though `build_changelog` installs git — the git there is only for towncrier's `git add` call, not for history. The local iteration story (`dagger call build-docs --src=. --version=dev` with uncommitted changes) depends on frontmatter dates. ### Local iteration ```bash dagger call build-docs --src=. --version=dev export --path=./docs-dev.tar.gz tar tf docs-dev.tar.gz | head -20 ``` ## Deployment and Testing - [x] `dagger call build-docs --src=. --version=dev` produces valid 1.1MB tarball (149 HTML pages) - [x] Pre-commit hooks pass (including new `docs-check-frontmatter`) - [ ] Full `workflow_dispatch` run after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/157
2 KiB
2 KiB
| title | date-modified | tags | |||
|---|---|---|---|---|---|
| Update Tailscale ACLs | 2026-02-07 |
|
Update Tailscale ACLs
How to modify Tailscale access control policies for the tailnet.
Prerequisites
- Pulumi CLI installed (
brew install pulumi) - Access to 1Password blumeops vault (for OAuth credentials)
Edit the Policy
The ACL policy lives in pulumi/policy.hujson (HuJSON format with comments).
Common changes:
Add a new ACL rule
{
"acls": [
// ... existing rules ...
{
"action": "accept",
"src": ["autogroup:admin"],
"dst": ["tag:newservice:*"]
}
]
}
Add a new tag
{
"tagOwners": {
// ... existing tags ...
"tag:newservice": ["autogroup:admin"]
}
}
Add a new group
{
"groups": {
// ... existing groups ...
"group:newgroup": ["user1@example.com", "user2@example.com"]
}
}
Preview and Apply
# Preview changes (always do this first)
mise run tailnet-preview
# Apply changes
mise run tailnet-up
# Skip confirmation prompt
mise run tailnet-up -- --yes
Verify
Check the Tailscale admin console at https://login.tailscale.com/ to confirm changes.
Common Patterns
Service-specific access
Grant access to a specific service port:
{
"action": "accept",
"src": ["group:users"],
"dst": ["tag:homelab:8080"]
}
SSH access
{
"ssh": [
{
"action": "check",
"src": ["autogroup:admin"],
"dst": ["tag:servers"],
"users": ["autogroup:nonroot"]
}
]
}
All ports for admins
{
"action": "accept",
"src": ["autogroup:admin"],
"dst": ["*:*"]
}
Troubleshooting
"Credential expired" error: Re-authenticate Pulumi with Tailscale. The OAuth token may need refreshing.
Changes not taking effect:
ACL changes are applied immediately. If a device isn't following new rules, try tailscale down && tailscale up on that device.