104 Commits

Author SHA1 Message Date
benvin 7b699c36b7 Merge pull request 'Add golib repository' (#89) from benvin/golib-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #89
2026-08-31 06:58:05 +10:00
unkin-agent c731f89dd4 Add golib repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Shared Go library for estate services, starting with a postgres pool and
migrations module.
2026-08-31 04:15:59 +10:00
benvin 2e9a5fb8d4 Merge pull request 'Add repospawner machine user scoped to terraform-git' (#88) from benvin/repospawner-user into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #88
2026-08-30 09:34:47 +10:00
benvin cb61d385e2 Merge pull request 'Add repospawner repository' (#87) from benvin/repospawner-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #87
2026-08-30 09:28:12 +10:00
unkin-agent dd26c861fc repospawner: mark the account restricted
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Defense in depth for the repo-creation bot: a restricted Gitea account can
only see repositories and organisations it is explicitly added to, so a
leaked repospawner token cannot enumerate or read the rest of the estate.
The repospawner team grant on terraform-git already covers everything the
bot does, so nothing it needs is lost.
2026-08-30 00:55:01 +10:00
unkin-agent 4b30ba0cfd Add repospawner machine user scoped to terraform-git
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
repospawner opens repo-creation PRs against terraform-git and needs no other
access, so give it its own identity and a per-repo team instead of adding it to
the org-wide agents team.

- add config/git.unkin.net/user/repospawner.yaml (limited visibility, not admin,
  no org/repo creation), mirroring the unkin-agent bot posture
- add config/git.unkin.net/unkin/team/repospawner.yaml granting write on the
  terraform-git repository only (include_all_repositories false)
2026-08-30 00:48:10 +10:00
unkin-agent 0e9f2a1279 Add repospawner repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-30 00:40:44 +10:00
benvin a38a792f69 Merge pull request 'Add mediamark repository' (#86) from benvin/mediamark-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #86
2026-08-29 21:13:42 +10:00
unkin-agent cd006fdff6 mediamark: make repo public and require woodpecker PR checks
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-29 21:08:48 +10:00
unkin-agent ae41bf6cdc repository: add mediamark repo
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-29 20:57:05 +10:00
benvin 98169c7cf8 Merge pull request 'Archive terraform-nomad repository' (#85) from benvin/archive-nomad into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #85
2026-08-23 22:36:29 +10:00
unkin-agent 960e88cd7e Archive terraform-nomad repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
The terraform-nomad project is being decommissioned; mark it archived
in Gitea so it becomes read-only, consistent with other retired repos
in this config (e.g. rpmbuild-template).
2026-08-23 22:34:03 +10:00
benvin 9ac6edd0a1 Merge pull request 'Run plan without acquiring the Consul state lock' (#84) from benvin/lockfree-plan into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #84
2026-08-23 22:31:30 +10:00
unkin-agent f83bdff5e6 Run plan without acquiring the Consul state lock
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline was successful
PR plan pipelines were failing with "Error acquiring the state lock"
when they collided with a concurrent apply (or another plan) holding
the lock on the same Consul-backed state.

- plan: pass -lock=false to terragrunt plan; apply/apply-if-changes keep locking
2026-08-23 22:21:11 +10:00
benvin e4a65e18ab Merge pull request 'Make clickhouse-tools and logviewer public and require CI status checks' (#83) from benvin/repo-defaults into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #83
2026-08-23 16:56:12 +10:00
unkin-agent 54fb6154af Require all three woodpecker PR contexts (build/test/pre-commit)
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-23 16:43:18 +10:00
unkin-agent 3cbfac7017 Make clickhouse-tools and logviewer public and require CI status checks
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Both repos do not need to be private, and their main branches should
only accept PRs whose Woodpecker pipeline passed.

- private: false for clickhouse-tools and logviewer
- require ci/woodpecker/pr/build status check on main for both
2026-08-23 16:33:47 +10:00
benvin 58c67e69be Merge pull request 'Add clickhouse-tools repository' (#81) from benvin/clickhouse-tools-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #81
2026-08-23 12:40:12 +10:00
benvin 43a43b5f85 Merge pull request 'Add logviewer repository' (#82) from benvin/logviewer-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #82
2026-08-23 12:39:40 +10:00
unkin-agent d4018f6b7e Add logviewer repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-23 12:37:18 +10:00
unkin-agent 22f11a0bda Add clickhouse-tools repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-23 12:36:32 +10:00
benvin 78191e8eaf Merge pull request 'Add mediamover repository' (#80) from benvin/mediamover-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #80
2026-08-22 22:53:07 +10:00
unkin-agent 9f85070281 repository: add mediamover repo
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-22 22:37:46 +10:00
benvin fe7dec14e8 Merge pull request 'waitfordb: add branch protection on main' (#79) from benvin/waitfordb-branch-protection into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #79
2026-08-22 17:03:15 +10:00
unkin-agent dd02e3dc75 Require all three PR status checks for waitfordb
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
The waitfordb CI is being split into three Woodpecker PR workflows
(pre-commit, test, build), each exposing its own status context. Require
all three on the main branch protection so a PR cannot merge until
pre-commit, test, and build all pass. Mirrors node-lookup.
2026-08-22 15:16:48 +10:00
unkin-agent c44f90e1ff waitfordb: add branch protection on main
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Owners-only merge, benvin approval, and the required Woodpecker PR check.
Requires the repo to be activated in Woodpecker so ci/woodpecker/pr/build
reports before a PR can merge.
2026-08-22 15:05:31 +10:00
benvin 6e6dcc1f25 Merge pull request 'Add waitfordb repo' (#78) from benvin/waitfordb-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #78
2026-08-22 13:29:00 +10:00
unkin-agent 7fa013160a repository: add waitfordb repo
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
waitfordb is a small Go tool shipped as a container image, used as a Kubernetes
initContainer to block an app from starting until its database is ready. It
replaces the hand-written psql-in-a-shell init steps on the arrstack.

Creates the unkin/waitfordb Gitea repo (Go tool settings mirroring node-lookup:
public, squash merges, delete branch after merge). branch_protection is omitted
on creation to avoid requiring Woodpecker status checks that do not exist until
the first pipeline runs; it will be added in a follow-up PR once CI is green.
2026-08-22 12:52:25 +10:00
benvin 62a9f092d1 Merge pull request 'Add terraform-provider-vault-secrets-arrstack repo' (#77) from benvin/add-terraform-provider-arrstackvaultsecret into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #77
2026-08-19 21:50:14 +10:00
unkin-agent b178a5cb37 Rename repo to terraform-provider-vault-secrets-arrstack
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Align the repo name with the vault-secrets-<engine> provider naming used by
the ghp/netbox provider repos (the canonical form for this engine).
2026-08-19 21:40:00 +10:00
unkin-agent 16e8d4cb18 Add terraform-provider-arrstackvaultsecret repo
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
terraform-provider-arrstackvaultsecret is a bespoke Terraform provider that
configures the new arrstack Vault/OpenBao secrets engine (engine config +
roles) from terraform-vault, following the per-engine-provider convention
used by the gpg/litellm/rancher vault-secret providers. It needs a Gitea
repo to host its source before CI and releases can be wired up.

- add config/git.unkin.net/unkin/repository/terraform-provider-arrstackvaultsecret.yaml
- public repo, default branch main, delete-branch-after-merge, squash-only
- Owners-only merge with required pre-commit/build/test Woodpecker checks
2026-08-19 21:35:36 +10:00
benvin abc4f0c3f2 Merge pull request 'Add vault-plugin-secrets-arrstack repo' (#76) from benvin/add-vault-plugin-secrets-arrstack into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #76
2026-08-18 21:21:24 +10:00
unkin-agent 2365b182f4 Add vault-plugin-secrets-arrstack repo
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
vault-plugin-secrets-arrstack is a new Vault/OpenBao secrets engine that
dynamically mints arrproxy machine (per-user) API tokens, enabling
terraform-driven onboarding of *arr services. It needs a Gitea repo to
host its source before CI and releases can be wired up.

- add config/git.unkin.net/unkin/repository/vault-plugin-secrets-arrstack.yaml
- public repo, default branch main, delete-branch-after-merge, squash-only
- Owners-only merge with required pre-commit/build/test Woodpecker checks
2026-08-18 20:47:41 +10:00
benvin 6ecf771f8e Merge pull request 'Protect main on the arr forks + require Woodpecker build' (#75) from benvin/arr-forks-protection into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #75
2026-08-16 21:43:51 +10:00
unkin-agent 247bc5cba5 Protect main on the arr forks + require the Woodpecker build
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Now that each fork's Woodpecker image build is green, lock down main:
Owners-only merge, PRs must pass the build check, and the owner may push
main directly for upstream-fix maintenance merges.

- add branch_protection(main) to sonarr/radarr/prowlarr.yaml
- required check: ci/woodpecker/pr/docker; merge whitelist: Owners; push whitelist: unkinben
2026-08-16 21:29:40 +10:00
benvin a2f1a23d4d Merge pull request 'Add sonarr/radarr/prowlarr fork repositories' (#74) from benvin/arr-forks-repos into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #74
2026-08-16 18:25:03 +10:00
unkin-agent a0933a10b9 Add sonarr/radarr/prowlarr fork repositories
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Create the three arrstack source-fork repos (Postgres + stateless
multi-replica work). No branch protection yet so the initial upstream
mirror can be force-pushed; a follow-up PR adds protection + required
Woodpecker checks once CI exists.
2026-08-16 17:48:52 +10:00
benvin 4837985a31 Merge pull request 'add arrproxy repository' (#73) from benvin/arrproxy-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #73
2026-08-16 15:37:38 +10:00
unkin-agent 9f04426601 arrproxy: tighten repo description
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-16 15:34:21 +10:00
unkin-agent 20f22deb51 arrproxy: apply standard branch protection + squash merge
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Match the sibling Go service repo convention (node-lookup/encapi):
require the woodpecker pre-commit/test/build PR status checks,
squash merge style, and restrict merge/approval to Owners/benvin.
2026-08-16 15:27:07 +10:00
unkin-agent 7df105be9c add arrproxy repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-16 14:57:28 +10:00
benvin fe66aa2840 Merge pull request 'rename ghp tf provider repo -> terraform-provider-vault-secrets-ghp' (#72) from benvin/ghp-provider-rename into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #72
2026-08-15 20:20:59 +10:00
unkin-agent f66d824f12 rename ghp tf provider repo to terraform-provider-vault-secrets-ghp
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Match the vault-secrets-netbox naming convention: repo
terraform-provider-vault-secrets-ghp, provider source address
git.unkin.net/unkin/vault-secrets-ghp, resources ghp_secret_backend /
ghp_secret_role. Renames the repo (the only content was an unmerged scaffold PR,
which is re-pushed to the new repo).
2026-08-15 20:13:41 +10:00
benvin 5916d4316f Merge pull request 'add ghp Vault engine repos (plugin + terraform provider)' (#71) from benvin/ghp-vault-repos into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #71
2026-08-15 18:58:15 +10:00
unkin-agent fafba02948 add ghp Vault engine repos (plugin + terraform provider)
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
P4 steps 5-6: a Vault/OpenBao secrets engine that mints scoped ghp access tokens
via ghp's admin API (authenticating with the static admin service token now
deployed), plus its Terraform provider. Mirrors the vault-plugin-secrets-gitea /
terraform-provider-giteavaultsecret pair.
2026-08-15 18:24:40 +10:00
benvin 8531d3e263 Merge pull request 'git: add plugin-docker-buildx repository' (#70) from benvin/plugin-docker-buildx-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #70
2026-08-15 15:16:39 +10:00
unkin-agent cfc4f6a92f git: add plugin-docker-buildx repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-15 15:01:21 +10:00
benvin b2168a999b Merge pull request 'git: add autobackup-operator repository' (#69) from benvin/autobackup-operator-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #69
2026-08-13 23:36:30 +10:00
unkin-agent c0d8d3d112 git: add autobackup-operator repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Provision the Gitea repo for the autobackup-operator, a Kubernetes
operator that creates S3 buckets and backup schedules from
backups.unkin.net/* annotations on PVCs and CNPG clusters. Matches the
convention of sibling operator repos (private=false, squash merges,
main branch protection with pre-commit/test/build checks).
2026-08-13 23:31:39 +10:00
benvin e50d01402e Merge pull request 'git: add ghp fork repository' (#68) from benvin/ghp-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #68
2026-08-13 22:59:09 +10:00
unkin-agent 07b236fb98 git: add ghp fork repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Fork goodtune/ghp into git.unkin.net/unkin/ghp to carry an
admin-service-token auth patch for the Vault ghp token engine.
2026-08-13 22:49:58 +10:00
benvin 63be799b84 Merge pull request 'Add agent-tools repository' (#67) from benvin/agent-tools-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #67
2026-08-12 21:32:46 +10:00
unkin-agent e69288f348 agent-tools: shorten description
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-12 21:31:56 +10:00
unkin-agent 3844cf9406 agent-tools: require CI status checks + squash merge (mirror node-lookup)
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-12 21:26:35 +10:00
unkin-agent 62e6aa1a85 repository: add agent-tools repo for agentpr/watchpr CLI tools
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
2026-08-12 21:22:21 +10:00
benvin 8ed3dc8348 Merge pull request 'repository: require CI status check on jellyfin-ha-src main' (#66) from benvin/jellyfin-required-ci into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #66
2026-08-11 21:31:29 +10:00
benvin dcf30f3036 Merge pull request 'repository: stop migration_mirror_interval planning an update every run' (#65) from benvin/branch-protection-whitelist-drift into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #65
2026-08-11 21:25:28 +10:00
unkinben 7e25e5c3fa repository: require CI status check on jellyfin-ha-src main
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why: the jellyfin-ha-src main branch is protected but merges are not gated
on CI, so a pull request can be merged while its Woodpecker build is red.

How: add the ci/woodpecker/pr/ci context to the main branch protection
rule. The gitea provider computes enable_status_check from a non-empty
context list, so this turns required status checks on and blocks merge
until that exact check passes.
2026-08-11 21:23:21 +10:00
unkinben cad35f8d9b repository: stop migration_mirror_interval planning an update every run
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why:
- The go-gitea/gitea provider schema defaults migration_mirror_interval to
  "8h0m0s", but on read it writes repo.MirrorInterval straight back into state,
  and Gitea returns an empty MirrorInterval for non-mirror repositories.
- So the stored value ("") never matches the schema default ("8h0m0s"), and tofu
  plans an in-place update of every gitea_repository on every run even with no
  config change, keeping terraform-git plans perpetually dirty.
- migration_mirror_interval is the only migration_* field read back from the API;
  the rest are write-only migration options, so no sibling field churns.

Change:
- Add a targeted ignore_changes on migration_mirror_interval at the repository
  module so the provider default no longer fights Gitea's empty value, making
  every repository idempotent. It is a migration-only knob with no drift to track
  for these non-mirror repos.
2026-08-11 21:14:24 +10:00
benvin 6d937ae6cd Merge pull request 'repository: protect jellyfin-ha-src main branch' (#64) from benvin/jellyfin-ha-src-protection into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #64
2026-08-11 20:45:40 +10:00
unkinben db62cd1b26 repository: protect jellyfin-ha-src main branch
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Now that the source fork is seeded on main with the pinned upstream tree and a
pristine upstream ref, enable branch protection on main: block direct pushes
and require an Owners approval so future changes land through reviewed pull
requests.
2026-08-10 23:55:07 +10:00
benvin 5bf5f9d85e Merge pull request 'repository: add jellyfin-ha-src source fork' (#63) from benvin/jellyfin-ha-src into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #63
2026-08-10 23:43:04 +10:00
unkinben 4ca5145d50 repository: add jellyfin-ha-src source fork
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add the jellyfin-ha-src Gitea repository that carries the HA source fork of
ZoltyMat/jellyfin-ha with local patches, replacing the direct upstream clone
in the build pipeline.

Declare the repository as public with a main default branch and
delete-branch-after-merge enabled. Leave branch protection unset so the
initial fork seed can push the pinned upstream tree and a pristine upstream
ref directly to main; protection is added in a follow-up change once the fork
is seeded.
2026-08-10 23:35:19 +10:00
benvin 62596a8086 Merge pull request 'team: stop spurious edits that fail Gitea 1.26 permission validation' (#62) from benvin/gitea-provider-1.26 into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #62
2026-08-10 23:05:51 +10:00
unkinben df118383da team: stop spurious edits that fail Gitea 1.26 permission validation
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why:
- The go-gitea/gitea provider's SDK (v0.25.1, already shipped in provider
  0.8.1 on main) rejects a team permission of `none` on every EditTeam with
  "permission mode invalid"; it only accepts read/write/admin/owner
  client-side. The same check exists in the older 0.21.0 SDK, so the provider
  version is not the lever here.
- The team module's `units` (server returns an unordered space-separated
  string that never matches the provider's comma-ordered default) and
  `repositories` (provider sorts state; `include_all_repositories` populates
  every repo) drift on representation alone, so every apply plans an in-place
  update for every team.
- Each update calls EditTeam, which replays the stored `permission=none` kept
  by `ignore_changes` and fails, blocking apply for every non-owner team
  (docker/terraform/rpmbuild/puppet/pybuild/forgebot/agents) and the whole
  terraform-git apply, including the unkin-agent rollout.

Change:
- Add `units` and `repositories` to the team `ignore_changes` so provider
  representation churn no longer triggers an EditTeam call.
- Preserve each team's current on-server permission and repository membership
  (no access change).
2026-08-09 12:31:19 +10:00
benvin 15978c486f Merge pull request 'Import pre-existing Gitea branch protections' (#61) from benvin/imports-branch-protection into main
ci/woodpecker/push/apply Pipeline failed
Reviewed-on: #61
2026-08-09 12:01:51 +10:00
unkinben a43267532a Import pre-existing Gitea branch protections
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why:
- tofu apply fails with "Branch protection already exist" because the nine
  repos protected in #60 already have live branch protection in Gitea that is
  absent from Terraform state, so Terraform tries to create rules that already
  exist and Gitea rejects them.
- The pinned go-gitea/gitea provider 0.7.0 defines no importer for
  gitea_repository_branch_protection, so the existing rules cannot be adopted
  on that version.

How:
- Bump the gitea provider from 0.7.0 to 0.8.1, which adds an importer for
  gitea_repository_branch_protection (id format <org>/<repo>/<rule_name>).
- Add import blocks adopting the nine existing branch protections into state
  instead of recreating them.
2026-08-09 11:45:26 +10:00
benvin d7d6794a51 Merge pull request 'Add unkin-agent Gitea identity with write but no merge' (#59) from benvin/unkin-agent-identity into main
ci/woodpecker/push/apply Pipeline failed
Reviewed-on: #59
2026-08-08 23:34:24 +10:00
benvin 7c4defde29 Merge pull request 'Protect default branches of unprotected active repos' (#60) from benvin/protect-unprotected-repos into main
ci/woodpecker/push/apply Pipeline failed
Reviewed-on: #60
2026-08-08 23:17:44 +10:00
unkinben f720133314 Protect default branches of unprotected active repos
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why:
- An audit for the new unkin-agent bot (org-wide write) found repos with
  no branch_protection at all, so the agent (or anyone with write) could
  push straight to their default branch.
- The rest of the estate protects default branches; these repos were the gap.

How:
- Add enable_push: false + approval_whitelist_teams: Owners on each active
  repo's default branch, matching the estate's most common convention
  (PR-only; Owners can still open and approve).
- Omit status checks on these repos as none run CI, so a required context
  would permanently block merges.
- Leave the 8 archived repos untouched: they are read-only, nothing can push
  to them, and branch-protection creation on archived repos is rejected.
2026-08-08 22:58:54 +10:00
unkinben 4953142200 Add unkin-agent Gitea identity with write but no merge
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why: AI coding agents currently push and open PRs as Ben's own account
using Ben's token, so their work is unattributable and carries Ben's full
owner privileges. A dedicated least-privilege identity separates agent work
from Ben's and keeps merge a human decision.

How:
- add the unkin-agent bot user (non-admin, no org/repo creation, limited
  visibility); it authenticates via ephemeral vault-plugin-secrets-gitea
  tokens, not a stored password
- add the agents team granting Write on all unkin repositories, the minimum
  needed to push branches and open PRs; unkin-agent is its only member
- add merge_whitelist_teams Owners to every default-branch protection rule
  that lacked a merge whitelist, so Write no longer implies merge and only
  Owners (Ben) can merge across the estate
2026-08-08 22:37:52 +10:00
benvin 4be9899e8e Merge pull request 'Add NetBox Vault secrets engine + provider repositories' (#57) from benvin/netbox-vault-repos into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #57
2026-08-06 22:13:36 +10:00
benvin 04b5f5c3bd Merge pull request 'Rename terraform-ipam -> terraform-infra' (#58) from benvin/rename-terraform-infra into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #58
2026-08-05 19:40:17 +10:00
unkinben f54e63caee Rename terraform-ipam -> terraform-infra
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Scope grew beyond IPAM (devices, provisioning). Renaming the config file renames
the managed repo; the gitea provider archives the old terraform-ipam repo
(archive_on_destroy=true) and creates terraform-infra. terraform-ipam only holds an
auto-init README, so nothing is lost.

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-05 00:51:36 +10:00
unkinben 674e659080 Add vault-plugin-secrets-netbox + terraform-provider-vault-secrets-netbox repositories
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Provisions the two Gitea repos for the NetBox Vault secrets engine and its
paired Terraform provider.

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-05 00:44:33 +10:00
benvin 9e16d7c5de Merge pull request 'Add terraform-ipam repository' (#56) from benvin/ipam-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #56
2026-08-03 00:15:37 +10:00
unkinben 41ac196b89 Add terraform-ipam repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
New repo for YAML-driven NetBox IPAM + Kea DHCP management.

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-03 00:09:05 +10:00
benvin fb6f45faf0 Merge pull request 'Add vault-plugin-secrets-apptoken repository' (#55) from benvin/add-apptoken-plugin-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #55
2026-08-02 21:50:29 +10:00
unkinben 1dfbddab92 Add vault-plugin-secrets-apptoken repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Provision the Gitea repo for a generic app-token Vault/OpenBao secrets
engine that issues short-lived signed JWTs for self-made services,
replacing per-app static bearer Secrets. Mirrors the existing plugin repo
entries (squash-only, main branch protection, PR CI checks).

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-02 21:36:48 +10:00
benvin 52b39bf668 Merge pull request 'Add terraform-provider-kea repository' (#54) from benvin/terraform-provider-kea-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #54
2026-08-02 19:37:00 +10:00
unkinben 5a2529f0c4 Add terraform-provider-kea repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Provisions the Gitea repo for the Terraform provider wrapping the
kea-operator KeaAPI (subnets + client classes).

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-02 19:33:07 +10:00
benvin 3b229deb1b Merge pull request 'Add kea-operator Gitea repository' (#53) from benvin/kea-operator-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #53
2026-08-02 17:22:40 +10:00
unkinben 8fc5105d10 Add kea-operator Gitea repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Provision the repo for the new Kea DHCP Kubernetes operator that replaces
the ISC dhcpd PXE-boot VM.

- add config/git.unkin.net/unkin/repository/kea-operator.yaml
Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-02 16:56:38 +10:00
benvin c920451844 Merge pull request 'argocd-apps: require the vector-test PR check' (#52) from benvin/argocd-vector-test-required into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #52
2026-07-30 21:10:33 +10:00
unkinben 493c33483b argocd-apps: require the vector-test PR check
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-30 21:09:39 +10:00
benvin 58dafb7ac2 Merge pull request 'Add bootapi-images repository' (#51) from benvin/bootapi-images-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #51
2026-07-30 21:03:18 +10:00
unkinben 68584acffb Add bootapi-images repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
bootapi-images builds the AlmaLinux node rootfs tarballs that bootapi's
image-based provisioning (liveimg) unpacks, and on a v* tag publishes
almalinux9-node-<ver>.tar.zst to the artifactapi rootfs-images local repo. Split
out of bootapi-templates so that repo stays templates-only.

Modeled on the bootapi-templates entry: public, squash, delete-branch-after-merge,
main protection requiring the pre-commit + lint Woodpecker checks and benvin
approval.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-30 20:55:45 +10:00
benvin bd66d55c99 Merge pull request 'Add bootapi-templates repository' (#50) from benvin/bootapi-templates-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #50
2026-07-28 23:08:51 +10:00
unkinben fe662001c5 Add bootapi-templates repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
bootapi-templates holds the live kickstart/iPXE templates and the distro catalog
that bootapi pulls via git-sync (every ~3m, like argocd), so template/OS changes
ship without rebuilding bootapi. Its CI validates that every template parses and
renders for every catalog distro (via 'bootapi validate') plus shellcheck on the
%post snippets.

Modeled on the bootapi repo entry: public, squash, delete-branch-after-merge,
main protection requiring the pre-commit + validate Woodpecker checks and benvin
approval.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-28 22:35:46 +10:00
benvin c0fc234a1c Merge pull request 'Add bootapi repository' (#49) from benvin/bootapi-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #49
2026-07-28 18:41:49 +10:00
unkinben d53b6841e1 Add bootapi repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
bootapi is a Go service replacing Cobbler's PXE/kickstart side: it renders
kickstart files and iPXE boot scripts from NetBox data and serves them to
PXE-booting hosts. The ENC half already moved to encapi; bootapi covers the
provisioning/boot half.

Adds config/git.unkin.net/unkin/repository/bootapi.yaml modeled on encapi:
public repo, squash merges, delete-branch-after-merge, main branch protection
requiring the pre-commit/test/build Woodpecker checks and benvin approval.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-28 16:58:21 +10:00
benvin 38849ba79f Merge pull request 'vault provider: skip child token creation in CI' (#48) from benvin/vault-skip-child-token into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #48
2026-07-27 22:03:28 +10:00
unkinben 0385d2a1ef vault provider: skip child token creation in CI
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 21:52:24 +10:00
benvin c068f56eca Merge pull request 'Add logarchiver repository' (#47) from benvin/logarchiver-repo into main
ci/woodpecker/push/apply Pipeline failed
Reviewed-on: #47
2026-07-27 21:48:24 +10:00
benvin 99b13d81bc Merge pull request 'user: add gitea-vault-admin site-admin bot with one-time Vault KV seeding' (#46) from benvin/gitea-vault-admin into main
ci/woodpecker/push/apply Pipeline failed
Reviewed-on: #46
2026-07-27 21:47:31 +10:00
benvin 9cee6024d3 Add logarchiver repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Registers the logarchiver Gitea repo: a k8s Go service that archives
raw logs from NATS JetStream to S3 as zstd-compressed, GPG-encrypted,
indexed objects, plus a CLI to search and retrieve them. It replaces the
Vector archiver leg of the centralized logging stack (argocd-apps #296)
with zstd compression, OpenPGP encryption keyed from the Vault GPG
secrets engine, and a ClickHouse-backed searchable index.

- Add config/git.unkin.net/unkin/repository/logarchiver.yaml modeled on
  the existing Go-service repos (encapi, cephrgw-operator): public repo,
  main default branch, squash merges, branch protection requiring the
  woodpecker pre-commit/test/build PR checks with benvin approval.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 21:36:15 +10:00
unkinben bd7bef9f99 user: add gitea-vault-admin site-admin bot and one-time Vault KV seeding
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Why: the vault-plugin-secrets-gitea engine needs a purpose-built Gitea
site-admin credential to mint and delete per-user tokens. Provision that
account and its Vault seed here so the credential is generated once and never
exposed. Ben asked that the password be written to Vault exactly once and
never updated afterwards.

Change:
- Add config/git.unkin.net/user/gitea-vault-admin.yaml: a local site-admin bot
  (admin: true, limited visibility, no org/repo creation).
- Extend the user module with an optional vault_seed_path/vault_seed_mount: when
  set, write the account's generated password to Vault KV as admin_username and
  admin_password via vault_kv_secret_v2, with lifecycle ignore_changes on
  data_json so the write is create-only and never churns (random_password
  already never regenerates). This keeps the seed stable and prevents a re-apply
  from overwriting a password later rotated out-of-band by rotate-root.
- Add the hashicorp/vault provider (module plus root generate block); it reads
  VAULT_ADDR and VAULT_TOKEN already exported by the Makefile k8s login.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 19:26:24 +10:00
benvin 96092cf7a1 Merge pull request 'Manage Gitea users; add teabot personality bot accounts' (#45) from benvin/teabot-users into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #45
2026-07-27 17:33:25 +10:00
unkinben fd82876f5f Manage Gitea users; add teabot personality bot accounts
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add a data-driven 'user' config kind so Gitea accounts are declared as
config/git.unkin.net/user/<name>.yaml, mirroring the existing repository
and team kinds. Wire the yaml into a new modules/user submodule that
creates a gitea_user (provider go-gitea/gitea 0.7.0, already pinned).

The provider's user resource requires a password; generate a per-user
random_password so nothing sensitive is hardcoded and only a placeholder
lives in state (tokens come later from vault-plugin-secrets-gitea).

Provision teabot's implementer and reviewer personality accounts with a
conservative posture: not site admins, no org creation, no repo creation,
limited profile visibility.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 17:22:19 +10:00
benvin ee08fd5585 Merge pull request 'Add gitea vault engine + provider repositories' (#44) from benvin/gitea-vault-repos into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #44
2026-07-27 00:40:20 +10:00
unkinben 4c5fe742f0 Add gitea vault engine + provider repositories
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Create the Gitea repositories for the new vault-plugin-secrets-gitea
secrets engine and its companion terraform-provider-giteavaultsecret,
mirroring the rancher pair's settings.

- add config/git.unkin.net/unkin/repository/vault-plugin-secrets-gitea.yaml
- add config/git.unkin.net/unkin/repository/terraform-provider-giteavaultsecret.yaml

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 00:34:28 +10:00
benvin aa9b2eac80 Merge pull request 'Add teabot repository (file)' (#43) from benvin/teabot-repo-add into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #43
2026-07-26 23:22:20 +10:00
benvin 3f39229c29 Merge pull request 'Add vault-tools repository' (#41) from benvin/vault-tools-repo into main
ci/woodpecker/push/apply Pipeline was successful
Reviewed-on: #41
2026-07-26 23:19:36 +10:00
unkinben 42187f86d5 Add vault-tools repository
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-26 23:18:08 +10:00
98 changed files with 1066 additions and 8 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ init:
plan: init
@$(call vault_env) && \
terragrunt run --all --parallelism 4 --non-interactive plan
terragrunt run --all --parallelism 4 --non-interactive plan -- -lock=false
apply-if-changes: init
@$(call vault_env) && \
+8
View File
@@ -33,6 +33,14 @@ locals {
})
if length(regexall("/team/", file_path)) > 0
}
user = {
for file_path, content in local.all_configs :
"${split("/", file_path)[0]}/${trimsuffix(basename(file_path), ".yaml")}" => merge(content, {
username = trimsuffix(basename(file_path), ".yaml")
gitea_url = split("/", file_path)[0]
})
if length(regexall("/user/", file_path)) > 0
}
branch_protection = merge([
for file_path, content in local.all_configs : {
for idx, rule in try(content.branch_protection, []) :
@@ -0,0 +1,16 @@
description: "CLI tools for orchestrator PR automation as unkin-agent"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
approval_whitelist_users:
- "benvin"
@@ -2,3 +2,8 @@ description: "Sudaporn's Research Data visualisation, normalised "
private: false
default_branch: "master"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "master"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -2,3 +2,8 @@ description: "Sudaporn's Research Data visualisation, normalised"
private: false
default_branch: "master"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "master"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -4,9 +4,12 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/kubeconform"
- "ci/woodpecker/pr/vector-test"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,16 @@
description: "arrstack reverse proxy: oauth2-gated UI + per-user API-key broker"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/build"
approval_whitelist_users:
- "benvin"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "master"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,16 @@
description: "Kubernetes operator that provisions S3 buckets and backup schedules from backups.unkin.net/* annotations on PVCs and CNPG clusters"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/build"
approval_whitelist_users:
- "benvin"
@@ -2,3 +2,8 @@ description: "Monorepo of small single-purpose CLI tools sharing a common librar
private: false
default_branch: "main"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,15 @@
description: "Builds AlmaLinux node rootfs tarballs for bootapi image-based provisioning; on v* tag publishes almalinux9-node-<ver>.tar.zst to the artifactapi rootfs-images repo."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/lint"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,15 @@
description: "Live kickstart/iPXE templates + distro catalog for bootapi. Pulled by bootapi via git-sync; CI validates templates render for every distro."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/validate"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,16 @@
description: "PXE/kickstart boot service replacing Cobbler. Renders kickstart + iPXE from NetBox data over HTTP. Go API."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/build"
approval_whitelist_users:
- "benvin"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,14 @@
description: "CLI tools (chcat, chtail, chgrep) for searching, filtering and tailing logs in the ClickHouse log store"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -1,3 +1,8 @@
description: "Docker image to be used in Kubernetes as a developers container"
private: false
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -3,3 +3,8 @@ private: false
default_branch: "master"
default_delete_branch_after_merge: true
repo_template: true
branch_protection:
- rule_name: "master"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -0,0 +1,17 @@
description: "Fork of goodtune/ghp (GitHub proxy) with unkin patches"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
# Whitelist-push: the owner can push main directly (needed for the initial
# push and the fork's maintenance workflow); everyone else must open a PR.
enable_push: true
push_whitelist_users:
- "unkinben"
# PRs to main must pass the fork build/vet pipeline before merge.
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -0,0 +1,14 @@
description: "Shared Go library for estate services: postgres, http service kit, vault and gitea clients"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
@@ -1,3 +1,8 @@
description: "A repository for building initrd.img in docker "
private: false
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,11 @@
description: "Source fork of ZoltyMat/jellyfin-ha carrying HA patches"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/ci"
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -0,0 +1,16 @@
description: "Kubernetes operator for managing Kea DHCP clusters, subnets, and PXE client classes"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/build"
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,16 @@
description: "Archives raw logs from NATS JetStream to S3 as zstd-compressed, GPG-encrypted, indexed objects; plus a CLI to search and retrieve them. Go service + CLI."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/build"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,14 @@
description: "Web UI for the ClickHouse log store: fuzzy find, tail and SQL-filter logs (logviewer.unkin.net)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,14 @@
description: "Keyboard-centric web UI to mark media as cheeztv (kids) content via hardlinks; oauth2-proxy fronted, talks to sonarr/radarr APIs"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,10 @@
description: "Media copy/move tool: Go API + UI spawning per-file k8s Jobs from mediafs CephFS to media PVCs; doubles as a Ceph aggregate-bandwidth test"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
# Whitelist-push: the owner can push main directly (needed for the initial
# push and the fork's maintenance workflow); everyone else must open a PR.
enable_push: true
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -0,0 +1,14 @@
description: "Woodpecker plugin-docker-buildx image with the internal Vault-PKI CA baked in (trusts artifactapi)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
approval_whitelist_teams:
- "docker"
@@ -0,0 +1,16 @@
description: "Private fork of Prowlarr: Postgres backend + stateless multi-replica for the arrstack"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
# Owner can push main directly for fork maintenance (upstream-fix merges);
# everyone else opens a PR that must pass the Woodpecker image build.
enable_push: true
push_whitelist_users:
- "unkinben"
status_check_contexts:
- "ci/woodpecker/pr/docker"
@@ -2,3 +2,8 @@ description: "A fastapi service to present puppet services via an api"
private: false
default_branch: "master"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "master"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,16 @@
description: "Private fork of Radarr: Postgres backend + stateless multi-replica for the arrstack"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
# Owner can push main directly for fork maintenance (upstream-fix merges);
# everyone else opens a PR that must pass the Woodpecker image build.
enable_push: true
push_whitelist_users:
- "unkinben"
status_check_contexts:
- "ci/woodpecker/pr/docker"
@@ -0,0 +1,14 @@
description: "API service that opens terraform-git PRs for new repo requests via kube Jobs; oauth2-proxy'd status UI"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
@@ -2,3 +2,8 @@ description: "Package the internal ca-certificates"
private: false
default_branch: "master"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "master"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,16 @@
description: "Private fork of Sonarr: Postgres backend + stateless multi-replica for the arrstack"
private: true
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
# Owner can push main directly for fork maintenance (upstream-fix merges);
# everyone else opens a PR that must pass the Woodpecker image build.
enable_push: true
push_whitelist_users:
- "unkinben"
status_check_contexts:
- "ci/woodpecker/pr/docker"
@@ -1,3 +1,8 @@
description: "A stack of microservices that aim to offer a distributed streaming service."
private: false
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
enable_push: false
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
@@ -4,6 +4,8 @@ default_branch: "main"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,16 @@
description: "YAML-driven Terraform for NetBox IPAM, networks, and devices + Kea DHCP"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/plan"
approval_whitelist_users:
- "benvin"
approval_whitelist_teams:
- "Owners"
@@ -2,6 +2,7 @@ description: "Manage nomad with Terraform"
private: false
default_branch: "master"
default_delete_branch_after_merge: true
archived: true
branch_protection:
- rule_name: "master"
enable_push: false
@@ -3,6 +3,8 @@ private: false
default_delete_branch_after_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,21 @@
description: "Terraform provider for the Vault/OpenBao Gitea token secrets engine (giteavaultsecret)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,16 @@
description: "Terraform provider for the kea-operator KeaAPI: manage Kea DHCP subnets and PXE client classes."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_users:
- "benvin"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,21 @@
description: "Terraform provider to manage the arrstack Vault secrets engine (config + roles)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,21 @@
description: "Terraform provider for the Vault/OpenBao ghp token secrets engine (vault-secrets-ghp)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,21 @@
description: "Terraform provider for the Vault/OpenBao NetBox token secrets engine (pairs with vault-plugin-secrets-netbox)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -5,4 +5,6 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
@@ -5,6 +5,8 @@ default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,21 @@
description: "HashiCorp Vault / OpenBao secrets engine issuing short-lived signed JWT app tokens (per-app roles, offline JWKS validation) for self-made services"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,21 @@
description: "Vault/OpenBao secrets engine minting dynamic arrproxy per-user API tokens"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,21 @@
description: "HashiCorp Vault / OpenBao secrets engine for ghp: mints ephemeral, scoped access tokens via the ghp admin API, authenticating as a static admin service token"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -0,0 +1,21 @@
description: "HashiCorp Vault / OpenBao secrets engine for Gitea: mints ephemeral, scoped per-user access tokens via the admin API, with seeded-admin basic-auth and root password rotation"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,21 @@
description: "HashiCorp Vault / OpenBao secrets engine for NetBox API tokens (seeded admin + dynamic per-user tokens with expiry-aligned leases)"
private: false
default_branch: "main"
default_delete_branch_after_merge: true
# Squash-only: the gitea provider has no "default merge style", so we restrict
# the allowed styles to squash to force it.
allow_merge_commits: false
allow_rebase: false
allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
approval_whitelist_teams:
- "Owners"
@@ -10,6 +10,8 @@ allow_rebase_explicit: false
allow_squash_merge: true
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/pre-commit"
@@ -0,0 +1,16 @@
description: "Monorepo of Go CLI tools for managing Vault tokens across multiple vault instances: vctl (login/renew per-context tokens) and vctx (context-aware wrapper around the vault CLI)."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,16 @@
description: "A small Go tool, shipped as a container image, used as a Kubernetes initContainer to block an app from starting until its database is ready."
private: false
default_branch: "main"
default_delete_branch_after_merge: true
default_merge_style: "squash"
branch_protection:
- rule_name: "main"
merge_whitelist_teams:
- "Owners"
enable_push: false
status_check_contexts:
- "ci/woodpecker/pr/build"
- "ci/woodpecker/pr/test"
- "ci/woodpecker/pr/pre-commit"
approval_whitelist_users:
- "benvin"
@@ -0,0 +1,14 @@
# Grants the unkin-agent bot Write across every unkin repository so Ben's AI
# coding agents can push benvin/* branches and open pull requests. Write is the
# minimum Gitea permission that allows pushing branches + creating PRs.
#
# Merge stays reserved for Ben: branch protection on every default branch sets a
# merge whitelist of the Owners team only, so members of this team can push and
# open PRs but cannot merge or approve. Keep this team out of every merge and
# approval whitelist.
description: "AI coding agents -- push branches + open PRs, never merge"
permission: write
include_all_repositories: true
can_create_repos: false
members:
- unkin-agent
@@ -0,0 +1,16 @@
# Least-privilege access for the repospawner bot: Write on terraform-git only,
# which is the minimum Gitea permission that allows pushing a branch and opening
# a pull request. repospawner does its whole job here (repo-creation PRs), so it
# is kept out of the org-wide "agents" team (include_all_repositories: true).
#
# Merge stays reserved for Ben: branch protection on terraform-git's main sets a
# merge whitelist of the Owners team only. Keep this team out of every merge and
# approval whitelist.
description: "repospawner bot -- push branches + open PRs on terraform-git only"
permission: write
include_all_repositories: false
can_create_repos: false
repositories:
- terraform-git
members:
- repospawner
@@ -0,0 +1,21 @@
# Purpose-built Gitea site-admin bot for the vault-plugin-secrets-gitea engine.
# The engine seeds itself from this account's credentials (Basic Auth) to mint
# and delete per-user tokens for any user. Its randomly generated password is
# written ONCE to Vault KV (vault_seed_path below) and never updated after; the
# Vault gitea engine reads that seed only when first creating gitea/config and
# then rotates it (rotate-root) so only Vault holds the live password.
#
# A local (not external-auth) account with 2FA disabled is required so the
# engine can change the password via the admin API during rotate-root.
email: gitea-vault-admin@unkin.net
full_name: "Gitea Vault Admin"
description: "site-admin bot; credentials seeded to Vault for vault-plugin-secrets-gitea"
# Site admin so the engine may mint/delete tokens for any user. No org/repo
# creation; profile visible only to signed-in users.
visibility: limited
admin: true
allow_create_organization: false
max_repo_creation: 0
# Seed this account's generated password to Vault KV (mount "kv") at this path,
# where the Vault gitea secrets engine reads it at creation time. Written once.
vault_seed_path: "service/vault/au/syd1/secret_backend/gitea/config"
@@ -0,0 +1,22 @@
# Service identity for repospawner, which opens pull requests against this
# repository (terraform-git) to add new Gitea repositories on request. It is
# deliberately separate from unkin-agent so its access can stay scoped to the
# single repository it needs: the "repospawner" team (unkin/team/repospawner.yaml)
# grants Write on terraform-git and nothing else.
#
# Auth is via ephemeral API tokens minted by the vault-plugin-secrets-gitea
# engine -- no usable credential lives in this repo's state beyond the generated
# placeholder. Merge stays blocked by branch protection (merge whitelist =
# Owners only), so it can push branches and open PRs but never merge.
email: repospawner@unkin.net
full_name: "Repo Spawner"
description: "repospawner service identity -- opens repo-creation PRs against terraform-git"
# Conservative bot posture: not a site admin, cannot create orgs or repos,
# profile visible only to signed-in users.
visibility: limited
admin: false
# Restricted: account can only see repos/orgs it is explicitly added to -- the
# repospawner team grant on terraform-git is all it needs.
restricted: true
allow_create_organization: false
max_repo_creation: 0
@@ -0,0 +1,14 @@
# teabot "implementer" personality account.
# Used by the teabot daemon (unkin/teabot) to open issues/PRs as a distinct
# identity so its work is attributable. Auth is via an API token issued
# out-of-band (vault-plugin-secrets-gitea / static KV) -- no token or usable
# password lives in this repo's state beyond the generated placeholder.
email: teabot-implementer@unkin.net
full_name: "Teabot Implementer"
description: "teabot implementer bot -- automated agent (unkin/teabot)"
# Conservative bot posture: not a site admin, cannot create orgs or repos,
# profile visible only to signed-in users.
visibility: limited
admin: false
allow_create_organization: false
max_repo_creation: 0
@@ -0,0 +1,14 @@
# teabot "reviewer" personality account.
# Used by the teabot daemon (unkin/teabot) to review pull requests as a distinct
# identity so its reviews are attributable. Auth is via an API token issued
# out-of-band (vault-plugin-secrets-gitea / static KV) -- no token or usable
# password lives in this repo's state beyond the generated placeholder.
email: teabot-reviewer@unkin.net
full_name: "Teabot Reviewer"
description: "teabot reviewer bot -- automated agent (unkin/teabot)"
# Conservative bot posture: not a site admin, cannot create orgs or repos,
# profile visible only to signed-in users.
visibility: limited
admin: false
allow_create_organization: false
max_repo_creation: 0
@@ -0,0 +1,18 @@
# Shared identity used by Ben's AI coding agents to submit work (branches, pull
# requests, issues, comments) as a distinct, attributable, least-privilege
# account instead of Ben's own login. Auth is via ephemeral API tokens minted by
# the vault-plugin-secrets-gitea engine (gitea/creds/unkin-agent) -- no token or
# usable password lives in this repo's state beyond the generated placeholder.
#
# Write access comes from the "agents" team (team/agents.yaml); merge is blocked
# by branch protection (merge whitelist = Owners only), so the agent can open and
# comment on PRs/issues but never merge or approve.
email: unkin-agent@unkin.net
full_name: "Unkin Agent"
description: "shared identity for automated AI coding agents -- write, never merge"
# Conservative bot posture: not a site admin, cannot create orgs or repos,
# profile visible only to signed-in users.
visibility: limited
admin: false
allow_create_organization: false
max_repo_creation: 0
+1
View File
@@ -21,4 +21,5 @@ inputs = {
repository = local.config.repository
branch_protection = local.config.branch_protection
team = local.config.team
user = local.config.user
}
+9
View File
@@ -11,6 +11,15 @@ provider "woodpecker" {
server = "https://ci.k8s.syd1.au.unkin.net"
}
# Address + token come from VAULT_ADDR / VAULT_TOKEN in the environment (the
# Makefile authenticates via k8s auth before running terragrunt). Used to seed
# bot-account credentials into Vault KV (see modules/user vault_seed_path).
# skip_child_token: the CI token from the k8s auth role cannot create child
# tokens (apply fails with "failed to create limited child token").
provider "vault" {
skip_child_token = true
}
terraform {
backend "consul" {
address = "https://consul.service.consul"
+47
View File
@@ -203,3 +203,50 @@ import {
id = "144"
}
# Branch protections that already exist in Gitea but are not yet in state.
# Import id format is <organisation>/<repository>/<rule_name>.
import {
to = module.branch_protection["git.unkin.net/unkin/app-sudaporn-research-individual/master"].gitea_repository_branch_protection.this
id = "unkin/app-sudaporn-research-individual/master"
}
import {
to = module.branch_protection["git.unkin.net/unkin/app-sudaporn-research-normalised/master"].gitea_repository_branch_protection.this
id = "unkin/app-sudaporn-research-normalised/master"
}
import {
to = module.branch_protection["git.unkin.net/unkin/benvin-utils/main"].gitea_repository_branch_protection.this
id = "unkin/benvin-utils/main"
}
import {
to = module.branch_protection["git.unkin.net/unkin/container-devcompute/main"].gitea_repository_branch_protection.this
id = "unkin/container-devcompute/main"
}
import {
to = module.branch_protection["git.unkin.net/unkin/docker-template/master"].gitea_repository_branch_protection.this
id = "unkin/docker-template/master"
}
import {
to = module.branch_protection["git.unkin.net/unkin/initbuilder/main"].gitea_repository_branch_protection.this
id = "unkin/initbuilder/main"
}
import {
to = module.branch_protection["git.unkin.net/unkin/puppetapi/master"].gitea_repository_branch_protection.this
id = "unkin/puppetapi/master"
}
import {
to = module.branch_protection["git.unkin.net/unkin/rpmbuild-internal-ca-certificates/master"].gitea_repository_branch_protection.this
id = "unkin/rpmbuild-internal-ca-certificates/master"
}
import {
to = module.branch_protection["git.unkin.net/unkin/streamstack/main"].gitea_repository_branch_protection.this
id = "unkin/streamstack/main"
}
+21
View File
@@ -38,6 +38,27 @@ module "repository" {
depends_on = [module.organisation]
}
module "user" {
source = "./modules/user"
for_each = var.user
username = each.value.username
email = each.value.email
login_name = each.value.login_name
full_name = each.value.full_name
description = each.value.description
visibility = each.value.visibility
admin = each.value.admin
restricted = each.value.restricted
active = each.value.active
allow_create_organization = each.value.allow_create_organization
max_repo_creation = each.value.max_repo_creation
must_change_password = each.value.must_change_password
vault_seed_path = each.value.vault_seed_path
vault_seed_mount = each.value.vault_seed_mount
}
module "team" {
source = "./modules/team"
@@ -3,7 +3,7 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
}
}
@@ -3,7 +3,7 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
}
}
@@ -3,7 +3,7 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
}
}
@@ -17,4 +17,9 @@ resource "gitea_repository" "this" {
website = var.website
autodetect_manual_merge = var.autodetect_manual_merge
archive_on_destroy = true
lifecycle {
# migration_mirror_interval defaults to "8h0m0s" but Gitea returns an empty MirrorInterval for non-mirror repos, so the read-back never matches and plans a spurious update every run; it is a migration-only knob with no drift to track here.
ignore_changes = [migration_mirror_interval]
}
}
@@ -3,7 +3,7 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
}
}
+2 -1
View File
@@ -8,7 +8,8 @@ resource "gitea_team" "this" {
repositories = var.repositories
lifecycle {
ignore_changes = [permission]
# Gitea's SDK rejects the stored permission=none on any EditTeam call; units/repositories only churn on provider representation (unordered units string, sorted/include-all repos), so ignore them to avoid spurious edits that would fail.
ignore_changes = [permission, units, repositories]
}
}
@@ -3,7 +3,7 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
}
}
@@ -0,0 +1,58 @@
# The gitea_user resource requires a password argument. These accounts
# authenticate via API tokens (issued out-of-band by the
# vault-plugin-secrets-gitea engine, or a static KV entry initially), so the
# password is never used interactively. It is generated here so nothing
# sensitive is hardcoded, and kept only in state.
resource "random_password" "this" {
length = 32
special = true
override_special = "!@#%^&*()-_=+"
}
resource "gitea_user" "this" {
username = var.username
login_name = coalesce(var.login_name, var.username)
email = var.email
password = random_password.this.result
full_name = var.full_name
description = var.description
visibility = var.visibility
admin = var.admin
restricted = var.restricted
active = var.active
allow_create_organization = var.allow_create_organization
max_repo_creation = var.max_repo_creation
must_change_password = var.must_change_password
# Do not e-mail the (possibly unmonitored) bot address on creation.
send_notification = false
lifecycle {
# The provider cannot read the password back from the API; ignore it so the
# account never shows perpetual drift once created.
ignore_changes = [password]
}
}
# Optional one-time seed of the account's credentials to Vault KV, for the
# vault-plugin-secrets-gitea engine to consume when it first creates gitea/config.
# random_password never regenerates (no keepers), so the seed is stable; the
# ignore_changes below makes the write strictly create-only, so a later
# rotate-root on the Vault side (which diverges the live password from this seed)
# is never clobbered by a re-apply here.
resource "vault_kv_secret_v2" "seed" {
count = var.vault_seed_path != null ? 1 : 0
mount = var.vault_seed_mount
name = var.vault_seed_path
data_json = jsonencode({
admin_username = var.username
admin_password = random_password.this.result
})
lifecycle {
ignore_changes = [data_json]
}
}
@@ -0,0 +1,17 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.8.1"
}
random = {
source = "hashicorp/random"
version = ">= 3.5"
}
vault = {
source = "hashicorp/vault"
version = ">= 4.3"
}
}
}
@@ -0,0 +1,87 @@
variable "username" {
description = "Username of the Gitea account (immutable; changing it recreates the user)"
type = string
}
variable "login_name" {
description = "Login name, may differ from username. Defaults to the username."
type = string
default = null
}
variable "email" {
description = "E-Mail address of the user"
type = string
}
variable "full_name" {
description = "Display / full name of the user"
type = string
default = ""
}
variable "description" {
description = "Profile description of the user"
type = string
default = ""
}
variable "visibility" {
description = "Profile visibility: public, limited or private"
type = string
default = "limited"
}
variable "admin" {
description = "Whether the user is a site administrator"
type = bool
default = false
}
variable "restricted" {
description = "Restricted users can only access repositories/orgs they are explicitly added to"
type = bool
default = false
}
variable "active" {
description = "Whether the account is active"
type = bool
default = true
}
variable "allow_create_organization" {
description = "Whether the user may create organisations"
type = bool
default = false
}
variable "max_repo_creation" {
description = "Maximum number of repositories the user may create (0 disallows creation)"
type = number
default = 0
}
variable "must_change_password" {
description = "Force a password change on first login"
type = bool
default = false
}
variable "vault_seed_path" {
description = <<-EOT
Optional. When set, the account's generated password is seeded ONCE to Vault
KV at this secret name (under vault_seed_mount) as admin_username +
admin_password, for the vault-plugin-secrets-gitea engine to consume at
creation time. The write is create-only; subsequent changes are ignored so
the seed never churns (and never overwrites a rotated password).
EOT
type = string
default = null
}
variable "vault_seed_mount" {
description = "KV v2 mount holding the seeded credential (used only when vault_seed_path is set)"
type = string
default = "kv"
}
+9 -1
View File
@@ -3,11 +3,19 @@ terraform {
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
version = "0.8.1"
}
woodpecker = {
source = "Kichiyaki/woodpecker"
version = "0.5.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.5"
}
vault = {
source = "hashicorp/vault"
version = ">= 4.3"
}
}
}
+21
View File
@@ -64,6 +64,27 @@ variable "branch_protection" {
}
variable "user" {
description = "Map of Gitea user accounts to create"
type = map(object({
username = string
email = string
login_name = optional(string)
full_name = optional(string, "")
description = optional(string, "")
visibility = optional(string, "limited")
admin = optional(bool, false)
restricted = optional(bool, false)
active = optional(bool, true)
allow_create_organization = optional(bool, false)
max_repo_creation = optional(number, 0)
must_change_password = optional(bool, false)
vault_seed_path = optional(string)
vault_seed_mount = optional(string, "kv")
}))
default = {}
}
variable "team" {
description = "Map of teams to create"
type = map(object({