chore: retire TemporalWorker CRD — agent-harness-worker and Forgejo build workflow removed
This commit is contained in:
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
# device-flow-demo.sh -- showcase Authentik OAuth 2.0 Device Authorization
|
||||
# Grant (RFC 8628) end to end, pure curl + jq.
|
||||
#
|
||||
# Requires: curl, jq
|
||||
#
|
||||
# Usage:
|
||||
# CLIENT_ID=<device-flow provider client_id> ./scripts/device-flow-demo.sh
|
||||
#
|
||||
# Steps:
|
||||
# 1. Requests a device_code + user_code from Authentik.
|
||||
# 2. Prints the verification URL + user_code for approval in a browser.
|
||||
# 3. Polls the token endpoint until approved, then prints the access_token.
|
||||
#
|
||||
# NOTE on quoting: the device_code returned by Authentik can contain raw
|
||||
# quote, backslash, and backtick characters. Always pass it to curl via
|
||||
# --data-urlencode from a shell variable (curl encodes it safely). Never
|
||||
# re-embed it inside another quoted string (a Python literal, a second shell
|
||||
# layer, etc) -- that is what breaks it.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
AUTHENTIK_URL="${AUTHENTIK_URL:-https://authentik.riotpiao.com}"
|
||||
CLIENT_ID="${CLIENT_ID:?CLIENT_ID is required}"
|
||||
SCOPE="${SCOPE:-openid}"
|
||||
|
||||
JQ_DEVICE_CODE='.device_code'
|
||||
JQ_USER_CODE='.user_code'
|
||||
JQ_VERIFICATION_URI='.verification_uri'
|
||||
JQ_EXPIRES_IN='.expires_in'
|
||||
JQ_INTERVAL='.interval'
|
||||
JQ_ERROR='.error'
|
||||
JQ_ERROR_DESC='.error_description'
|
||||
JQ_ACCESS_TOKEN='.access_token'
|
||||
|
||||
echo "==> Requesting device code from ${AUTHENTIK_URL}..."
|
||||
if ! DEVICE_RESP="$(curl -sS --fail \
|
||||
"${AUTHENTIK_URL}/application/o/device/" \
|
||||
--data-urlencode "client_id=${CLIENT_ID}" \
|
||||
--data-urlencode "scope=${SCOPE}")"; then
|
||||
echo "FAILED: device code request errored (bad CLIENT_ID or unreachable Authentik)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEVICE_CODE="$(echo "$DEVICE_RESP" | jq -r "$JQ_DEVICE_CODE")"
|
||||
if [ -z "$DEVICE_CODE" ] || [ "$DEVICE_CODE" = "null" ]; then
|
||||
echo "FAILED: no device_code in response: $DEVICE_RESP" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_CODE="$(echo "$DEVICE_RESP" | jq -r "$JQ_USER_CODE")"
|
||||
VERIFICATION_URI="$(echo "$DEVICE_RESP" | jq -r "$JQ_VERIFICATION_URI")"
|
||||
EXPIRES_IN="$(echo "$DEVICE_RESP" | jq -r "$JQ_EXPIRES_IN")"
|
||||
INTERVAL="$(echo "$DEVICE_RESP" | jq -r "${JQ_INTERVAL} // 5")"
|
||||
|
||||
echo
|
||||
echo " Go to: ${VERIFICATION_URI}"
|
||||
echo " Enter code: ${USER_CODE}"
|
||||
echo " (expires in ${EXPIRES_IN}s)"
|
||||
echo
|
||||
echo "==> Polling for approval every ${INTERVAL}s..."
|
||||
|
||||
DEADLINE=$(( $(date +%s) + EXPIRES_IN ))
|
||||
ACCESS_TOKEN=""
|
||||
|
||||
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
|
||||
sleep "$INTERVAL"
|
||||
|
||||
TOKEN_RESP="$(curl -sS \
|
||||
"${AUTHENTIK_URL}/application/o/token/" \
|
||||
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
|
||||
--data-urlencode "device_code=${DEVICE_CODE}" \
|
||||
--data-urlencode "client_id=${CLIENT_ID}")"
|
||||
|
||||
ERR="$(echo "$TOKEN_RESP" | jq -r "${JQ_ERROR} // empty")"
|
||||
|
||||
if [ -z "$ERR" ]; then
|
||||
ACCESS_TOKEN="$(echo "$TOKEN_RESP" | jq -r "$JQ_ACCESS_TOKEN")"
|
||||
break
|
||||
elif [ "$ERR" = "authorization_pending" ]; then
|
||||
echo " ...still waiting for approval"
|
||||
continue
|
||||
elif [ "$ERR" = "slow_down" ]; then
|
||||
INTERVAL=$((INTERVAL + 5))
|
||||
continue
|
||||
else
|
||||
DESC="$(echo "$TOKEN_RESP" | jq -r "${JQ_ERROR_DESC} // ${JQ_ERROR}")"
|
||||
echo "FAILED: $DESC" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$ACCESS_TOKEN" ]; then
|
||||
echo "Timed out waiting for approval." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==> Got a token. Decoded payload:"
|
||||
python3 - "$ACCESS_TOKEN" <<'PYEOF'
|
||||
import sys, json, base64
|
||||
tok = sys.argv[1].split(".")[1]
|
||||
tok += "=" * (-len(tok) % 4)
|
||||
print(json.dumps(json.loads(base64.urlsafe_b64decode(tok)), indent=2))
|
||||
PYEOF
|
||||
|
||||
echo
|
||||
echo "==> Example use as a bearer token:"
|
||||
echo "curl https://api.riotpiao.com/v1/reasoning/chat/completions \\"
|
||||
echo " -H \"Authorization: Bearer ${ACCESS_TOKEN}\" \\"
|
||||
echo " -H \"Content-Type: application/json\" \\"
|
||||
echo " -d \"{\\\"model\\\":\\\"reasoning\\\",\\\"messages\\\":[{\\\"role\\\":\\\"user\\\",\\\"content\\\":\\\"hi\\\"}]}\""
|
||||
echo
|
||||
echo "NOTE: api.riotpiao.com does not yet validate Authentik JWTs -- Kongs"
|
||||
echo "model routes currently enforce a static key-auth credential instead."
|
||||
echo "See k8s/apps/api/DEVICE-GRANT-PLAN.md Phase 2 for the pending work."
|
||||
@@ -0,0 +1,72 @@
|
||||
Implement **Stage 1 only** of the approved plan at
|
||||
`/Users/rockliang/.claude/plans/fluttering-cooking-thunder.md`. Read that file first — it is
|
||||
the spec. Do not implement Stage 2, 3 or 4.
|
||||
|
||||
## Already done, do not redo
|
||||
|
||||
Stage 0 passed. The Forgejo registry returns distinct, correctly-ordered image
|
||||
creation timestamps, so `newest-build` is viable:
|
||||
|
||||
```
|
||||
rock/api-gateway v0.0.0 2026-08-20T05:19:48.655Z
|
||||
rock/api-gateway v0.1.0 2026-08-20T06:57:11.943Z
|
||||
rock/api-gateway v0.1.1 2026-08-20T07:10:13.093Z
|
||||
```
|
||||
|
||||
Note the images are multi-arch OCI indexes: reading `created` means descending
|
||||
index -> amd64 manifest -> config blob.
|
||||
|
||||
## Scope: Stage 1 = A1, A2, A4, A6, B, C1
|
||||
|
||||
- **A1** — in `~/workplace/homelab`, replace the per-repo Forgejo entry in
|
||||
`k8s/argocd/projects/homelab-project.yaml` `sourceRepos` with a wildcard
|
||||
`https://forgejo.riotpiao.com/rock/*`.
|
||||
- **A2** — add an Argo `Application` at sync-wave `-1` that syncs
|
||||
`k8s/argocd/projects/`. Nothing owns that directory today, which is why the
|
||||
AppProject only ever reaches the cluster by hand.
|
||||
- **A4** — Forgejo webhook to `https://argocd.riotpiao.com/api/webhook` with a
|
||||
shared secret stored in `argocd-secret` (SOPS/ksops). Register it on
|
||||
`rock/homelab` and `rock/homelab-frontend`.
|
||||
- **A6** — add the `forgejo-registry` dockerconfigjson pull secret for any
|
||||
namespace that needs it, as a new `*.enc.yaml` listed in
|
||||
`k8s/argocd/secrets/secret-generator.yaml`. It currently exists only in `api`.
|
||||
- **B + C1** — in `~/workplace/homelab-frontend`: delete the dead
|
||||
`.github/workflows/ci.yml`, add `.forgejo/workflows/ci.yaml` and
|
||||
`.forgejo/workflows/build.yaml`, and add a multi-stage distroless `Dockerfile`
|
||||
(none exists today).
|
||||
|
||||
Stage 1 stops before Argo CD Image Updater. Do **not** install it and do not add
|
||||
image-updater annotations — that is Stage 2.
|
||||
|
||||
## Hard constraints
|
||||
|
||||
- **`runs-on: docker`.** That is the runner's only registered label. The existing
|
||||
`.github/workflows/ci.yml` uses `ubuntu-latest`, which is exactly why it has
|
||||
never executed once.
|
||||
- **No git tags, ever.** Image tag is the commit short SHA: `$(git rev-parse --short HEAD)`.
|
||||
Do not use `git describe`, do not create or push tags, do not use `latest`.
|
||||
- **Build workflow only builds and pushes.** No manifest write-back, no git push
|
||||
from CI, no `[skip ci]` guard needed.
|
||||
- **GitOps only.** No `kubectl apply`, no `helm upgrade`, no local `terraform apply`.
|
||||
`kubectl --dry-run=server` and read-only `kubectl get`/`logs` are fine.
|
||||
- **Never `git reset --hard`.**
|
||||
- Push directly to `main`, no PRs, no branches. The cluster repo has three
|
||||
remotes — `origin` is Forgejo, `github` is GitHub. Push to both; they are
|
||||
currently in sync at `43483da`.
|
||||
- Match surrounding file style. This repo comments the *why* on non-obvious
|
||||
config, and `kustomization.yaml` uses explicit `resources:` allowlists — a file
|
||||
you add and forget to list is silently dropped.
|
||||
|
||||
## Verify before claiming done
|
||||
|
||||
- `kubectl kustomize` each directory you touch.
|
||||
- `kubectl apply --dry-run=server -f` every manifest you add or change.
|
||||
- Confirm the AppProject wildcard is live:
|
||||
`kubectl -n argocd get appproject homelab -o jsonpath='{.spec.sourceRepos}'`
|
||||
- Push an empty commit to `homelab-frontend` and confirm the build job actually
|
||||
runs on the Forgejo runner and pushes `forgejo.riotpiao.com/rock/api-gateway:<short-sha>`.
|
||||
A workflow that does not trigger is the single most likely failure here.
|
||||
- Report what you verified with real command output, not assertions. If a step
|
||||
fails, say so and stop rather than working around it.
|
||||
|
||||
Kubeconfig: `/Users/rockliang/workplace/homelab/cluster-config/kubeconfig`.
|
||||
Reference in New Issue
Block a user