Replace all <MagicMock ...> values with empty strings or sensible defaults:
- BooleanVar mocks → false
- StringVar mocks for known fields (cluster_name, db_host_port, db_namespace,
db_username, cluster_env) → correct defaults
- All other StringVar mocks → empty string
Files are now safe to parse and commit. Protected going forward by
_validate_cfg_values() added in ef20c8a.
Add common_core_default_config_path() which resolves the correct knoe.cfg
path (k3d/k3s/gke) from KNOE_MODE/DEPLOYMENT_MODE/CLUSTER_ENV env vars,
falling back to the first cfg file found under conf/. Replaces the
hardcoded knoe.cfg reference in the config-loading comment.
Co-authored-by: Junie <junie@jetbrains.com>
docs/completed/ — new directory; 9 shipped Junie briefs moved from
docs/plans/junie/ (02-k3s-prole-rename, 03-image-rename, 06-patch-garage,
07-init-cnpg-gke-sa-wiring, 13-podmonitor, 15-remove-dead-dashboard,
k3d-knoe-auth-dev-loop, phase2-oidc-gke-deploy, todo-1-cfg-save-path-bug).
docs/completed/README.md summarises all shipped work with dates/refs.
docs/plans/junie/README.md — updated to 2026-05-23; active/pending tables
reflect current state; shipped section now points to docs/completed/.
conf/service/knoe.cfg — new unattended deploy config for the service/k3s
environment (used by ./deploy.sh).
Co-authored-by: Junie <junie@jetbrains.com>
infrastructure/playbooks/pihole_flush_dns.yml — Ansible playbook to
flush Pi-hole DNS cache across inventory hosts.
infrastructure/logs/ — log directory for Ansible run output.
Co-authored-by: Junie <junie@jetbrains.com>
knoe/knoe_conf.py: normalize_environment() now recognises the gke_ prefix
(e.g. gke_plenary-truck-485623-p7_us-west3_knoe-dev-0) as the 'prod'
environment, matching real GKE kubecontext naming.
status.py: add _current_kubecontext() and _cfg_path_from_kubecontext()
so status.py auto-selects the correct cfg file based on the active
kubectl context without manual KNOE_CONF overrides.
Co-authored-by: Junie <junie@jetbrains.com>
Add _validate_cfg_values() to knoe/ui/screens/cfg.py — called before
_filter_cfg_values_for_persistence so MagicMock (or any non-str) widget
values raise TypeError instead of being silently str()-coerced into
conf/<mode>.cfg.
Also re-raise TypeError/ValueError from _save_knoe_cfg so the error
escapes the outer broad except-Exception handler.
New test: tests/installer/test_cfg_save_refuses_mock_values.py
- test_save_knoe_cfg_refuses_non_string_widget_values: MagicMock vars → TypeError
- test_save_knoe_cfg_real_strings_produce_clean_cfg: real _Var stubs → clean cfg
Fixes TODO-1 / tracked in docs/completed/todo-1-cfg-save-path-bug.md.
conf/k3d.cfg and conf/k3s.cfg still contain stale MagicMock values from
before this fix and must be regenerated before committing.
Co-authored-by: Junie <junie@jetbrains.com>
vault_samba_dns_admin_pass was stale — samba-tool commands were failing
with NT_STATUS_LOGON_FAILURE. Updated to match current AD Administrator
credential so the samba_dns role can manage records end-to-end.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All samba-tool dns commands were using -P (machine account Kerberos) which
silently failed with no error — tasks reported changed=0 but records were
never written. Switch to -U Administrator --password={{ samba_dns_admin_pass }}
which uses the vault-protected admin credentials that were already defined
but never wired up.
Also fix regex patterns in record parsers: samba-tool output uses `A: IP`
and `PTR: fqdn` format, not `A IP` / `PTR fqdn` (space-separated), so
updated regex_findall patterns to match `TYPE:\s+value`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- knoe-db.yaml: switch to CNPG-managed TLS cert with serverAltDNSNames
(pg.prole.org + knoe-db-rw cluster service) — removes static serverTLSSecret/serverCASecret
- dns.yml: add pg.prole.org A record to prole_k3s_dns_records (10.0.0.3, 10.0.0.6)
for Ansible-managed split-horizon DNS via Samba AD DC
- k3s.cfg: align KNOE_HOME paths to ~/dev/prole, add PROLE_KDC_* vars, remove
hardcoded KUBECTL_CONTEXT (kubeconfig current-context is authoritative)
- prod.cfg: add PROLE_KDC_STORAGE_CLASS = prole-iscsi
- onepassword.py: skip vault check gracefully when no 1Password session active (non-TTY)
- CLAUDE.md: document production postgres connection string and DNS/CA cert ops
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mermaid 10.7.0 chokes on bare <placeholder> in message text, parsing
it as HTML. Swap to square brackets in the cross-realm §4.3 sequence
diagram and the Gitea SPNEGO §5 diagram:
<svc-host>, <SPNEGO> (§4.3)
<blob> (§5)
Matches the same fix landed in knoe-db's docs/knoe-auth.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Companion to the canonical architecture doc at
~/dev/knoe-db/docs/knoe-auth.md. Documents what's specific to the
prole.org homelab deployment:
- Two upstream identity layers (Samba AD on myrddin for PROLE.ORG
Kerberos, Google Workspace knoey.com for OIDC)
- Three deploy targets (k3s on myrddin/merlin/gandalf, k3d for
engineer dev loop, GKE for production API endpoint)
- Hostname map (myrddin/morgana/zinfandel/db.prole.org/
git.prole.org/api.knoe.dev/db.knoe.dev/etc.)
- Cross-realm Kerberos trust setup (KNOE.LOCAL ↔ PROLE.ORG) via
infrastructure/playbooks/kerberos_trust_setup.yml — RC4 pin
explanation, kvno sync notes
- Gitea SPNEGO at git.prole.org with Mermaid flow diagram
- Supabase Studio status (db.prole.org Google OAuth working;
db.knoe.dev knoe-auth OIDC blocked on pg_oauth image)
- Engineer onboarding checklist (prole-specific steps)
- Known fragility / current incidents table
Mermaid sequence diagram covers the cross-realm flow end-to-end
(workstation → Samba KDC → MIT KDC → service ticket), distinct from
the canonical doc's generic version.
References the matching Junie briefs in knoe-db for the open
fragility items (pg_oauth-image-install, init-user-schemas-heredoc-fix,
deploy-pipeline-restore).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five fixes Junie surfaced while running the kdc-trust-reset-repeatable
Junie brief end-to-end (companion to commit 6f99f95). All hit during
the unattended `install.sh --mode k3s --reset` pipeline.
- knoe/core/milestones.py (KerberosMilestone):
For k3s and k3d modes, deploy the KDC pod via `init_kdc.sh start`
before running init_kerberos.sh. init_kerberos.sh only chains into
init_kdc.sh when PROLE_KDC_STANDALONE=1; without this hook the
cluster came up with no KDC pod and the cross-realm trust principals
had nowhere to land.
- knoe/milestone.py (Milestone._get_script_env):
Clear KUBECTL_CONTEXT in addition to KUBECONTEXT so stale entries
from a different machine's cfg don't override the kubeconfig's
own current-context.
- etc/knoe_cfg.sh (_knoe_read_cfg):
Skip KUBECTL_CONTEXT / KUBE_CONTEXT_NAME / KUBECONTEXT entries when
reading cfg in k3s mode. Same theme: kubeconfig current-context is
authoritative.
- etc/init_1password.sh + knoe/core/onepassword.py:
When running non-interactively (no TTY on stdin) and no `op`
session exists, skip rather than hang on `op signin`. Lets the
unattended pipeline proceed for k3s/k3d where in-cluster secrets
are managed separately from 1Password.
Co-authored-by: Junie <junie@jetbrains.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix _deployment_mode_hint() to correctly map cluster_env=service → k3s
mode. Previously 'service' was not handled in the normalized_env checks,
causing fallthrough to build.deploy_env='Dev' → mode='dev' → k3d
dependency required. On a k3s node (myrddin/merlin/gandalf) k3d is not
installed and the DependenciesMilestone fatally aborted the install.
Also fix get_required_dependency_ids(): k3s mode does not require k3d
(k3s is provisioned on the cluster nodes by Ansible, not by the
installer binary).
Fixes: install.sh --mode k3s --reset failing with
'Dependencies unresolved after install attempts. Required: k3d'
Co-authored-by: Junie <junie@jetbrains.com>
Mirrors the knoe-db commit `ff7546d` patches into the prole copy of
`etc/init_kdc.sh` so a re-run of `install.sh --mode k3s --reset` from
this repo produces a working cross-realm trust without manual cluster
surgery. The k3s cluster is provisioned from this repo, so the source
fix must live here (knoe-db remains canonical for GKE).
Changes to etc/init_kdc.sh:
1. Create BOTH cross-realm krbtgts in MIT, not just the outbound one.
The inbound `krbtgt/<REALM>@<TRUST_REALM>` (issued by Samba,
decrypted here) was missing entirely; without it, MIT cannot
decrypt inbound TGTs and the trust never carries traffic.
2. Pin both cross-realm krbtgts to RC4 (`arcfour-hmac:normal`). AES
keys depend on salt, and Samba's `<remote_realm>+UPN` salt does
not match MIT's `<local_realm>+<principal-no-realm>`; RC4 has no
salt so both sides converge from the password alone. Matches the
already-pinned Samba side (commit `ad1eced`).
3. Replace the broken "remote kadmin to Samba" reciprocal-trust block
with a documented no-op pointing at
`infrastructure/playbooks/kerberos_trust_setup.yml`. Samba AD
does not accept additions over MIT's kadmin protocol; the block
always failed with "Missing parameters in krb5.conf required for
kadmin client".
4. Switch the KDC data volume from emptyDir to a PVC
(claimName `knoe-kdc-data`, parameterized by
`$PROLE_KDC_STORAGE_SIZE` and `$PROLE_KDC_STORAGE_CLASS`).
State now survives pod restarts.
Adds Junie brief `docs/plans/junie/kdc-trust-reset-repeatable.md`
with four TDD acceptance criteria for an end-to-end --reset run on
the prole k3s cluster.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The matching knoe-db change (commit `ff7546d`) re-keys the MIT-side
cross-realm krbtgts with `-e arcfour-hmac:normal` to dodge the
AES-salt mismatch between Samba and MIT. For the trust to actually
carry traffic, the Samba side must also offer only RC4 on the
trust account — otherwise the client picks AES, the two sides
derive different keys from the same password (different salt
conventions), and TGT decryption fails.
Change `msDS-SupportedEncryptionTypes` from 28 (RC4+AES128+AES256)
to 4 (RC4 only). RC4 has no salt, so keys derive from the password
alone and both sides converge.
Updated docstring + summary debug print to reflect the new value
and the why.
Tracked alongside the Junie brief at
~/dev/knoe-db/docs/plans/junie/kdc-trust-reset-repeatable.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The post-rebrand realm is KNOE.LOCAL (matches knoe-db/etc/init_kdc.sh
already). The prole copy of init_kdc.sh was never updated, so the k3s
KDC pods on myrddin/merlin/gandalf got initialized with PROLE.LOCAL
realm baked into /etc/krb5.conf and /etc/krb5kdc/kdc.conf.
When chrisfu later wired up the Samba <-> in-cluster KDC cross-realm
trust playbook (kerberos_trust_setup.yml, targeting KNOE.LOCAL), the
trust was bidirectionally correct on the Samba side but the in-cluster
KDC could not decrypt the inbound krbtgt/KNOE.LOCAL@PROLE.ORG TGT
because its own realm was PROLE.LOCAL — different krbtgt key, every
TGS_REQ failed with "Decrypt integrity check failed" (manifested as
"Server not found in Kerberos database" in the client trace).
This commit fixes the source. To pick it up in-cluster, the active
KDC pod must be redeployed (its DB sits on an emptyDir volume so a
restart re-initializes against this default).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
klist shows the Kerberos principal (slash form
krbtgt/KNOE.LOCAL@PROLE.ORG), not the sAMAccountName underscore form
(krbtgt_KNOE.LOCAL). Update the summary debug task accordingly and
split the kvno hint into two stages (the cross-realm TGT first, then
a service ticket) so the user can isolate which step fails when
chasing salt/enctype issues.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The apt install task was positioned after the user-create step, so a
failure in create (or anywhere upstream) meant ldb-tools never got
installed. Move the apt task to the very top of the play, before the
cluster-secret lookups, so it always runs first on every invocation.
Drop the duplicate apt task that previously sat just before the
ldbmodify locator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The _trust_exists probe was still grepping samba-tool user list for
"krbtgt/KNOE.LOCAL" (slash form), but the actual sAMAccountName is
"krbtgt_KNOE.LOCAL" (underscore form, since slash is invalid in
sAMAccountName). _trust_exists was therefore always false, the create
step retried against the existing account on every re-run, and Samba
rejected it as a name collision.
Match against samba_account_name directly so re-runs route through
the setpassword branch instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
myrddin doesn't have ldbmodify installed — it's in the ldb-tools
Debian package, separate from samba-tool. Add an apt task ahead of
the locator so the package is in place before we try to use it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ldbmodify is part of the ldb-tools Debian package but is not in
root's default PATH under sudo on the Samba DC, so the user-attribute
modify step failed with "ldbmodify: command not found".
Add a locator task that probes the conventional install paths
(/usr/bin, /usr/sbin, /usr/local/bin, /opt/samba/bin) and falls back
to a depth-limited find under /usr and /opt. The downstream
ldbmodify invocation now uses the absolute path resolved here.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
samba-tool user create rejected the cluster-generated trust_shared_password
with "0000052D: Constraint violation - check_password_restrictions: the
password does not meet the complexity criteria". The trust password is
high-entropy random bytes from init_kdc.sh and may not satisfy AD's
"3 of 4 character categories" rule.
Wrap the create + setpassword steps in a block that:
1. reads the current `samba-tool domain passwordsettings show` state
2. flips complexity off if it was on
3. does the user write
4. always: restores complexity to its prior on/off state
The restore is in an `always:` clause so a failure inside the write
block does not leave the domain policy weakened.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
samba-tool user create rejected the sAMAccountName "krbtgt/KNOE.LOCAL"
with "samldb: sAMAccountName contains invalid '/' character". AD/Samba
disallow "/" in sAMAccountName even though Kerberos principal names
require it (krbtgt/REMOTE@LOCAL).
Switch storage name to "krbtgt_KNOE.LOCAL" and add an ldbmodify pass
that sets the canonical Kerberos identity on the same object:
userPrincipalName = krbtgt/KNOE.LOCAL@PROLE.ORG
servicePrincipalName = krbtgt/KNOE.LOCAL
msDS-SupportedEncryptionTypes = 28 (RC4+AES128+AES256)
Samba KDC resolves principals by UPN/SPN, so a TGS-REQ for
krbtgt/KNOE.LOCAL@PROLE.ORG will hit this account.
Note: key-salt parity with the MIT side is NOT guaranteed yet. Samba's
default salt for AES keys is REALM+UPN; MIT's default for cross-realm
krbtgt is REALM+"krbtgt"+REMOTE. If kvno fails with "decrypt integrity
check failed" we'll add a keytab-export/import step in a follow-up
rather than try to coerce Samba's salt at creation time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In --check mode the user-create step is skipped (it's mutating), so
the subsequent `samba-tool user show krbtgt/KNOE.LOCAL` probe fails
with rc=1 on a first dry-run because the user doesn't yet exist. Add
the same guarded failed_when used on the trust validate/show tasks
earlier: only fail when NOT (check_mode AND trust didn't pre-exist).
Real runs still fail hard on rc!=0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
samba-tool domain trust create --type=external only works against
another writeable AD-style DC; against an MIT KDC it fails with
"Failed to find a writeable DC for domain 'KNOE.LOCAL'". Our peer at
svc/auth in knoe-system is a plain Kerberos KDC, so we cannot use that
path.
Replace the trust create/validate/show steps with the supported
Samba-AD <-> MIT-KDC approach: create the inter-realm TGT principal as
an ordinary user account in Samba whose sAMAccountName is
"krbtgt/KNOE.LOCAL", with its password synced to the cluster Secret
knoe-system/knoe-kdc-secrets/trust_shared_password (which the MIT side
already keys against). Steps:
1. samba-tool user list (idempotency probe)
2. samba-tool user create krbtgt/KNOE.LOCAL ... (when missing)
3. samba-tool user setpassword krbtgt/KNOE.LOCAL (when present, to
re-sync after a cluster Secret rotation)
4. samba-tool user setexpiry --noexpiry
5. ldbmodify msDS-SupportedEncryptionTypes=28 (RC4+AES128+AES256
to match what init_kdc.sh sets on the MIT side)
6. samba-tool user show (smoke probe)
7. debug task prints the manual kvno smoke-test command
Cluster-secret lookups and krb5.conf [realms]/[domain_realm] edits are
unchanged - those parts were correct.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ansible --check mode auto-skips command/shell tasks unless they
explicitly opt in via check_mode:false. The two cluster-state lookups
(svc/auth ClusterIP and knoe-kdc-secrets/trust_shared_password) were
silently skipped during dry-run, leaving trust_kdc_ip empty and tripping
the assertion immediately.
Add check_mode:false to the five read-only tasks so a --check run can
still resolve cluster state and report what would change:
- Resolve trust_kdc_ip from cluster
- Resolve trust_shared_password from knoe-kdc-secrets
- samba-tool domain trust list (idempotency probe)
- samba-tool domain trust validate
- samba-tool domain trust show (smoke print)
The mutating create step (samba-tool domain trust create) keeps the
default behavior - skipped in check mode. validate/show gain a guarded
failed_when so a check-mode dry-run on a host where the trust does not
yet exist does not fail (the create was skipped, so a non-zero rc is
expected). Real runs still fail hard on rc!=0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The cross-realm trust playbook was written for the pre-rebrand realm name
PROLE.LOCAL, which no longer exists in the in-cluster KDC — the canonical
realm is now KNOE.LOCAL. The Samba AD trust was therefore never actually
established, leaving chrisfu@KNOE.LOCAL→chrisfu@PROLE.ORG service ticket
flows blocked (and blocking pg_oauth / db.prole.org Kerberos work).
Changes:
- Realm: PROLE.LOCAL → KNOE.LOCAL across all task names, vars, and the
krb5.conf [realms] / [domain_realm] blocks added on myrddin.
- samba_admin_password now resolves from the Ansible vault var
vault_samba_dns_admin_pass (group_vars/ad_dc/vault.yml) by default,
with SAMBA_ADMIN_PASSWORD env and -e overrides preserved for CI.
- trust_shared_password auto-resolves from the in-cluster Secret
knoe-system/knoe-kdc-secrets (key trust_shared_password) when not
passed explicitly — same Secret init_kdc.sh writes.
- Added [domain_realm] mapping for *.knoe.local → KNOE.LOCAL so Samba
can resolve in-cluster service principals.
- Added a final `samba-tool domain trust show` smoke step so a successful
run prints the visible trust state for log review.
Run:
ANSIBLE_VAULT_PASSWORD_FILE=$PWD/.vault_pass ansible-playbook infrastructure/playbooks/kerberos_trust_setup.yml
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
win_regedit writes HKLM keys without privilege escalation when
ansible_user is already a local/domain Administrator. become:true
with --ask-become-pass was overriding the playbook's runas method.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A single play with become:true uses sudo by default, which Ansible's
Windows exec wrapper rejects immediately at gather_facts. Split into:
- Play 1: hosts workstations,!workstations_windows — macOS/Linux, sudo
- Play 2: hosts workstations_windows — Windows, become_method:runas
Also set morgana to ansible_connection=local (running Ansible from
morgana itself, so SSH self-lookup fails DNS resolution).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add morgana and zinfandel (macOS) and morgoth + fairyland (Windows)
to the workstations inventory.
Extend workstation_kerberos.yml to handle Windows targets:
- Chrome and Edge AuthServerAllowlist set via win_regedit under
HKLM\SOFTWARE\Policies\{Google\Chrome,Microsoft\Edge}
- krb5.conf and plist tasks skip on Windows (uses built-in SSPI/AD)
- Windows hosts in [workstations_windows] group with WinRM/NTLM
transport; switch to kerberos transport after domain join
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gitea's AdminEditUser PATCH with source_id:0 resets the password field
as a side effect when applied to a SPNEGO-registered user (the auth
record re-initialisation clears the local password). Setting the
password before the promotion PATCH meant it was immediately wiped.
Move the 1Password recovery password step to run after the admin
promotion PATCH so the final Gitea state matches 1Password.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gitea's AdminEditUser endpoint requires email alongside login_name
and source_id. Without it the server returns 200 but silently
ignores the password field — causing login failures despite the
script reporting success.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 1Password password-setting block was nested inside path 2 of
promote_gitea_admin(). When path 1 found a cached gitea-admin-token
k8s secret, path 2 was skipped entirely and the recovery password
was never set in Gitea, leaving the user unable to log in.
Move the password-setting step to run unconditionally after all four
token paths complete. Uses _op_ensure_auth() so it degrades gracefully
in CI/headless environments where 1Password is unavailable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: 'defaults write' is not an onboarding path, it's a support
incident waiting to happen. Browser SPNEGO must be deployed via managed
policy at machine provisioning time, not per-user.
Changes:
etc/init_knoe_users.sh
- _op_ensure_auth(): tries OP_SERVICE_ACCOUNT_TOKEN env, k8s secret
'op-service-account-token', existing interactive session, and op-run
inheritance — in that order. Gives actionable instructions when none
work, including the correct 'op run --' invocation documented in k3s.cfg.
- gitea_ensure_password(): calls _op_ensure_auth() at entry; eliminates
the silent 'op not authenticated' failure path.
- Script header: documents 'op run -- bash etc/init_knoe_users.sh' as
the intended invocation for admin's laptop.
- 'Next steps' output: replaces 'defaults write' with reference to
workstation_kerberos.yml Ansible playbook.
infrastructure/playbooks/workstation_kerberos.yml (new)
- Deploys /etc/krb5.conf (PROLE.ORG realm, myrddin KDC) to all managed
endpoints.
- Deploys Chrome + Edge managed policy (AuthServerAllowlist) on macOS
and Linux — no per-user browser configuration ever required.
- Idempotent; run during laptop provisioning or re-run at any time.
- Targets 'workstations' Ansible group.
infrastructure/inventory/hosts.ini
- Adds [workstations] group with example entries and onboarding notes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SPNEGO auto-registration assigns a UUID@localhost placeholder email.
promote_gitea_admin() now detects the UUID pattern and replaces it with
the user's real configured email (KNOE_ADMIN_EMAIL / GITEA_ADMIN_EMAIL).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1Password credentials are optional (primary auth is Kerberos SPNEGO).
When op is not signed in on a headless server, show a WARN with a
helpful hint instead of an ERROR that suggests a hard failure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
log() writes to stdout, so log calls inside functions that return
values via printf/stdout contaminate the captured output.
gitea_helm_admin_token and gitea_ensure_password now redirect all
log() calls to stderr with >&2, keeping stdout clean for the
returned token/password string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gitea admin edit-user API requires email in PATCH body.
Fix HTTP status parser (tr -d vs broken cut -d_).
Add logging to user-existence check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gitea 1.22 requires explicit scopes in the token creation POST body.
Without them the API returns: "access token must have a scope".
Now requests: read/write:admin, read/write:user, read/write:issue,
read/write:repository — sufficient for admin user management.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a Deployment has been updated but the new pod keeps crashing before
configure-gitea runs (e.g. after a failed Helm upgrade), the running pod
still carries the old RS's password — which differs from the current
Deployment spec.
gitea_helm_admin_token() now walks: running-pod-RS → current-Deployment-spec,
trying each password until one produces a valid API token.
Also adds HTTP status + response body to gitea_api_token() failures so the
cause is visible without exposing credentials.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Gitea Helm chart (gitea-12.5.3) stores GITEA_ADMIN_USERNAME and
GITEA_ADMIN_PASSWORD as plain values in the configure-gitea init container
spec — not in a k8s Secret with key 'admin-password'. The previous code
looked for a non-existent secretKeyRef and returned empty, causing the
REST API bootstrap path to fail.
Now reads credentials via:
kubectl get deployment gitea ... env[?(@.name=="GITEA_ADMIN_PASSWORD")].value
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Gitea Helm chart names the admin credentials secret after the release
name alone, not '{release}-gitea'. For release name 'gitea' the secret is
just 'gitea'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>