This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
forgejo
git.alwaldend.com
Links
Repository configuration
Service Terraform consumes the shared repository catalog
for organization-owned repositories and named administrator/developer roles.
Vault continues to own OIDC login identities and service-specific access.
See service Terraform for identity validation, state adoption,
and the preserved automation grants.
Deployment
Forgejo is recreated on XCP-ng through Xen Orchestra, using the
src_infra_dc1_forgejo1 resource set provisioned by infra/xcp_ng.
VM provisioning authenticates to XO through Vault OIDC as Forgejo’s own
AppRole. Its exact synchronized user receives the resource-set membership
and an explicit administration ACL on the existing Forgejo VM; it does not
use the shared infrastructure administrator token. These bindings must be
applied by infra/xcp_ng/tf before Forgejo’s setup Terraform runs.
The canonical service URL is https://git.alwaldend.com. The previous
forgejo.alwaldend.com name remains available for existing clients.
The VM retains 192.168.10.40 and host1.forgejo.alwaldend.com. Its boot, Forgejo,
and Traefik disks are 20, 40, and 5 GiB respectively.
See Terraform setup for the template, network, storage,
and previous-state prerequisites. These commands create and configure a
fresh service; they do not restore the destroyed Proxmox VM’s data.
Fresh host hardening can outlast the ten-minute Vault token. Run host setup
and service deployment separately to obtain fresh authentication for each phase. Ansible
creates the missing Vault OIDC login source after starting Forgejo; existing
active Vault sources are preserved.
Before configuring Forgejo with Terraform, set TF_VAR_vault_oauth_source_id
to the verified ID of the active vault OIDC source, as described in
service Terraform. Its narrowly scoped Vault entity-read policy
must be provisioned first so the required external users can be created.
The guest disables IPv4 redirect acceptance so its traffic uses the configured
gateway. LAN clients must also retain symmetric routes: same-interface router
redirects can otherwise make connections fail. Router redirect policy and stale
client route caches must be resolved before relying on LAN access.
bazel_agent bazel run //infra/forgejo/tf_setup:tf_setup.apply
bazel_agent bazel run //infra/forgejo/ansible:ansible -- --skip-tags traefik,forgejo,forgejo_oidc
bazel_agent bazel run //infra/forgejo/ansible:ansible -- --tags traefik,forgejo,forgejo_oidc
bazel_agent bazel run //infra/forgejo/tf:tf.apply
1 -
Forgejo infrastructure specification
Purpose
Describe the Forgejo service owned by infra/forgejo, including Xen
Orchestra provisioning, Ansible service configuration, and Terraform account
and repository management. The tf_setup, ansible, and tf packages are
implementation stages of this owner. This baseline records source guarantees
and prerequisites; it does not verify live service health or restore earlier
Forgejo data.
Baseline revision: 550d7e79b1f5fdbc2b6017b75178471d6914082f.
Observed: 2026-09-08. Sources: owner README,
BUILD, and the implementation links below.
Requirements
Requirement: Scoped Xen Orchestra provisioning and disk checks
Setup SHALL provision Forgejo through Xen Orchestra in the
src_infra_dc1_forgejo1 resource set using the owner’s Vault AppRole and
packaged XO OIDC login flow. The VM SHALL declare 20 GiB boot, 40 GiB
Forgejo, and 5 GiB Traefik disks. Ansible SHALL verify the expected sizes
of xvdb and xvdc before applying the service roles that use them.
Sources: setup contract,
setup BUILD,
VM definition, and
deployment playbook.
Scenario: Detect unexpected Xen disk attachments
- WHEN Ansible gathers disks that do not match the expected Forgejo and
Traefik device names and sizes
- THEN its pre-task assertion fails before the service roles run
Requirement: Canonical service routing with legacy hostname support
Forgejo SHALL configure https://git.alwaldend.com/ as its canonical root
URL, bind its HTTP service to loopback port 3000, and enable the built-in
SSH service on port 3005. Traefik SHALL route the canonical hostname, the
inventory hostname, and forgejo.alwaldend.com to that HTTP service with
the Vault certificate resolver.
Sources: service variables,
Forgejo configuration, and
Traefik routing.
Scenario: Inspect support for existing Forgejo clients
- WHEN the Traefik routing template is rendered with the owner’s variables
- THEN the canonical and legacy hostnames resolve to the same configured
loopback service
- AND generated Forgejo URLs use the canonical Git hostname
Requirement: Guarded Vault OIDC bootstrap
The deployment playbook SHALL create a missing vault OIDC authentication
source after configuring the Forgejo service. It SHALL preserve one existing
active OAuth2 source and reject multiple matches, an inactive source, or a
source of another type. Creation SHALL obtain client credentials from the
injected environment and suppress secret-bearing task output.
Source: deployment playbook.
Scenario: Preserve an existing active authentication source
- WHEN Forgejo reports exactly one active OAuth2 source named
vault
- THEN the bootstrap accepts it and skips source creation
Scenario: Reject an ambiguous authentication source
- WHEN more than one
vault source is discovered, or its type or active
state is incompatible
- THEN the bootstrap assertion fails rather than selecting or replacing
an arbitrary source
Requirement: Vault identities and service grants with catalog named roles
Service Terraform SHALL require a verified positive integer Vault OAuth
source ID, discover login users from the owning Vault group through at most
two nested group levels, and use entity UUIDs as external login names.
Managed accounts SHALL be protected from deletion. The shared repository
catalog SHALL own named organization administrator and developer assignments
and organization-owned repository identities. Catalog members and Vault
access-group members SHALL belong to the discovered login population.
Existing Vault service-administrator, package-writer, and automation-writer
grants SHALL be retained; the src automation writer group SHALL resolve to
exactly one user. A catalog developer SHALL NOT also receive administrator
access through the retained Vault groups.
Sources: service Terraform contract,
users,
access validation, and
repository access.
The shared catalog contract owns repository
naming and named-role assignments; its
adoption change
records the source migration and pending verification.
Scenario: Reject unsupported group membership
- WHEN a login group extends beyond two nested levels, an access-group
member is outside the login population, or the automation writer count is
not one
- THEN the corresponding Terraform condition rejects the configuration
- AND Terraform does not silently omit the unsupported membership
Scenario: Resolve a catalog member through Vault
- WHEN a catalog administrator or developer is assigned organization access
- THEN that assignment uses the discovered account’s existing Vault entity
UUID and verified OAuth source mapping
- AND the assignment does not create a password-based replacement account
Scenario: Retain service access while restricting a named developer
- WHEN shared named roles are adopted
- THEN existing Vault service administration, package-writing, and
automation-writer grants remain configured
- AND the catalog developer receives feature-branch and pull-request
access without a default-branch push or merge bypass
- AND an overlapping administrator grant for that developer fails
validation
2 -
owned-dns Specification
Purpose
Define Forgejo DNS management through the owner’s tf_setup root,
including canonical declarations, scoped credentials, and offline source checks
before adopting live records.
Requirements
Requirement: Owner-local DNS configuration
The tf_setup root SHALL consume this owner’s canonical dnsconfig.json through
the shared DNS Terraform module, preserving declared record identities and views.
DNS resources SHALL default to disabled before adoption and SHALL remain enabled
by default after authorized adoption. Adoption SHALL bind existing provider records
to the owner’s state without adding, changing, replacing, or deleting DNS records.
Subsequent reconciliation of unchanged declarations SHALL preserve owned and
unrelated records.
Scenario: Inspect the preparatory configuration
- WHEN the checked-in root is evaluated with default inputs before adoption
- THEN it reads this owner’s declaration and disables managed DNS records
- AND the package contains the module and declaration inputs
Scenario: Inspect the adopted configuration
- WHEN the adopted root is evaluated with default inputs
- THEN it reads this owner’s declaration and enables managed DNS records
- AND the package contains the module and declaration inputs
Scenario: Adopt existing records
- WHEN the owner reviews the matched existing provider imports through
//infra/forgejo/tf_setup:dns.plan and :dns.show, then applies that saved
plan through :dns.apply
- THEN the owner state binds each declared record to its existing provider identity
- AND the reviewed adoption plan proposes no record additions, changes,
replacements, or deletions
Scenario: Reconcile unchanged declarations
- WHEN the adopted root plans unchanged declarations through
//infra/forgejo/tf_setup:dns.plan and applies its reviewed saved plan
through :dns.apply
- THEN it makes no DNS resource changes
- AND owned records retain their declared values and views while unrelated
records remain unchanged
Requirement: Scoped DNS execution and offline checks
The DNS wrapper SHALL select src_infra_dc1_forgejo1 through the repository AL
flow and keep secret values in injected variables. Dedicated DNS commands SHALL
select dns=1, retain the existing setup backend, and target module.dns without
starting unrelated service authentication. DNS apply SHALL consume only a
reviewed saved plan. Real DNS credentials and Vault policy grants SHALL be
prerequisites for operational Terraform calls. RouterOS DNS credentials SHALL be
isolated from unrelated RouterOS resources. The package SHALL expose a format
test that does not authenticate to Vault or contact DNS providers.
- WHEN the package format test executes
- THEN it checks the packaged Terraform configuration without live credentials
- AND existing non-DNS authentication and backend paths remain unchanged
- WHEN the wrapper selects its Terraform stage labels
- THEN it selects DNS credential injection through the owner AppRole with
dns=1, the existing setup backend, and the module.dns target
- AND disabled DNS resources do not imply offline provider configuration
3 - Ansible
Ansible setup
4 - Tf
Terraform config
This package configures Forgejo organizations, repositories, and access from
the shared repository catalog. The catalog owns named
administrators, developers, repository identities, and destination names.
Its Terraform module supplies the consumer’s
repository projection. First-party names remain stable across forges;
external fork and mirror names retain their original upstream identity.
Before applying, provision the Vault OIDC authentication source with Ansible and set
TF_VAR_vault_oauth_source_id to its verified positive numeric ID. Obtain the
ID from forgejo admin auth list on the instance using the service’s config
and work path; confirm that the vault source is active and uses OpenID
Connect with the expected Vault issuer. Do not assume source IDs survive
instance recreation.
Terraform discovers users from the authoritative Vault
src_infra_dc1_forgejo1_users login group, then looks up their entities,
following the PVE Terraform pattern. Discovery includes direct members and
two levels of nested groups, covering the current administrator membership.
Deeper nesting fails validation rather than silently omitting accounts.
Each account uses its Vault entity UUID as the OIDC login name and its
canonical username/email metadata. No local password or user impersonation is
used. The Forgejo AppRole receives the existing group/entity lookup permissions,
as PVE does. Apply those Vault group memberships before running this package.
The accounts are protected from Terraform deletion. OIDC group claims retain
ownership of administrator/restricted status after login. If a user already
exists outside this state, verify its source and login-name mapping before
importing it; never replace an existing account to resolve a name collision.
Resource addresses remain keyed by Vault entity name, preserving existing
managed users. The Forgejo service identity may already exist from the first
OIDC login: verify and import it before applying its newly discovered resource.
Named organization administrator and developer assignments come from the
catalog and must resolve to the discovered Vault login population. The
Developer team can read repositories, write feature branches, and open pull
requests. Its members receive no default-branch push or merge bypass.
Existing service administration remains sourced from
src_infra_dc1_forgejo1_admins and its direct child groups. Those grants are
retained for entities with a Vault AppRole alias, alongside catalog
administrators. The separate instance-wide OIDC administrator claim remains
owned by Vault. Validation rejects a catalog
developer who also receives administrator access through those Vault groups.
Package writers and the repository automation writer retain the grants from
src_infra_dc1_forgejo1_package_writers and
src_infra_dc1_forgejo1_src_writers. These dedicated groups avoid granting
service permissions to AppRole credential issuers. The existing singleton
collaborator still requires exactly one discovered writer. Access-group
members outside the login population also fail validation.
Repository adoption preserves remote IDs. The existing singleton repository
address has an explicit move to its catalog key, while account and service
access addresses remain stable. Import an existing untracked destination
before applying; never recreate it to resolve a name collision. Review the
full plan before execution and obtain separate approval for any deletion or
replacement. The retained branch rules continue to own administrator and
automation-writer exceptions.
bazel_agent bazel run //infra/forgejo/tf:tf.plan
bazel_agent bazel run //infra/forgejo/tf:tf.apply
5 - Tf setup
Terraform setup
This package creates a Xen Orchestra VM in the src_infra_dc1_forgejo1
resource set. infra/forgejo/al.lua authenticates with Forgejo’s own Vault
AppRole and the packaged XO OIDC login plugin. The plugin supplies a temporary
XO token and revokes it on shutdown; no infrastructure administrator token
is loaded. The setup HTTP backend remains owned by the same Forgejo config.
Before running this package, bootstrap the AppRole’s XO OIDC user and apply
its resource-set membership and existing VM ACL through infra/xcp_ng/tf.
Subjects are matched by immutable Vault entity UUID under the configured
OIDC issuer, not by login name or AppRole group membership. See
XO authentication.
Defaults select the imported Fedora 44 template, local storage and wired
network by name from the Forgejo resource set. Override
TF_VAR_xoa_template_name, TF_VAR_xoa_storage_name, and
TF_VAR_xoa_network_name for another assignment. Native provider lookups run
as Forgejo’s own identity and reject ambiguous matches. No infrastructure
UUID defaults are required; resolved IDs are passed to the XO API.
The template must contain a single boot disk no larger than 20 GiB, Fedora,
and cloud-init. Bootstrap installs Xen guest tools. The pinned Fedora 44
image uses predictable interface naming; xoa_guest_interface defaults to
enX0 for its first Xen interface and can be overridden for another template.
The selected network must carry 192.168.10.0/24; gateway and DNS default to
192.168.10.1 (the wired router) and can be overridden with the Terraform variables.
The PVE-based infra/cloud_init:xen_linux target supplies the shared Ansible
user and CA configuration through the repository template rule. Terraform adds
the hostname and static network configuration from ../dnsconfig.json. After cloud-init completes, verify that /dev/xvda is the
boot disk, /dev/xvdb is the 40 GiB Forgejo disk, and /dev/xvdc is the 5 GiB
Traefik disk before running Ansible, which creates filesystems on the latter
two devices.
The old Proxmox VM was destroyed and its obsolete Terraform state binding
was removed without issuing a destroy. This package now uses only XO.
There is no cross-provider state move or data restoration in this package.
bazel_agent bazel run //infra/forgejo/tf_setup:tf_setup.plan
bazel_agent bazel run //infra/forgejo/tf_setup:tf_setup.apply
Use this package’s dns.plan, dns.show, and dns.apply targets for the
scoped DNS workflow. They select dns=1, retain the
existing setup backend and src_infra_dc1_forgejo1 AppRole, and target
module.dns without starting unrelated service authentication. Inspect the
saved plan through dns.show and pass only that reviewed file to dns.apply.
dns_enabled defaults to true after verified adoption. Keep it enabled to
retain existing records; disabling it would propose deletion. Follow the
cutover procedure
for prerequisites, reconciliation, and recovery. The owner adoption change
records the historical import and DNS verification evidence; scoped DNS checks
do not establish service or VM health.