Secrets & connections (team guide)¶
How passwords, API keys, and service-to-service links work on this platform. Written for Rafael and Alex — simpler than Architecture → Secrets, but complete enough to understand day-to-day work and what not to commit.
Versión en español
Resumen al final de esta página: Resumen en español.
Two kinds of “passwords”¶
1. Human logins (you type in the browser)¶
These are normal user accounts. Not stored in git.
| Service | URL | Username | Where password is stored |
|---|---|---|---|
| Forgejo (git) | git.rafaelgonzalezalbes.com | alex or rafael.gonzalez.albes |
Forgejo database on VPS (hashed) |
| Plane (issues) | pm.rafaelgonzalezalbes.com | Your email | Plane database on VPS (hashed) |
| Uptime Kuma (monitoring) | status.rafaelgonzalezalbes.com | Per-user | Kuma SQLite on VPS |
| Woodpecker (CI) | ci.rafaelgonzalezalbes.com | — | No separate password — sign in with Forgejo OAuth |
| Docs | docs.rafaelgonzalezalbes.com | — | No login — static site, team URL only |
Registration is disabled on Forgejo and Plane. Only Rafael and Alex have accounts.
2. Machine secrets (automation, no human typing)¶
Used by CI pipelines, webhooks, and deploy scripts. Never paste these in chat or commit them.
| Category | Examples | Who needs them |
|---|---|---|
| CI deploy | SFTP password, SSH private key, Plane API key for CI comments | Woodpecker (per repo) |
| Platform stack | Woodpecker DB password, OAuth client secret, bridge tokens | VPS at deploy time (from SOPS) |
| Developer automation | Forgejo PAT, Plane PAT, Woodpecker token | local/.env on your PC only |
| Decryption | Age private key | Rafael’s PC + Woodpecker sops_age_key for infra deploy |
Where secrets are stored (full map)¶
flowchart TB
subgraph never_git [Never in git]
UserPW[User passwords\nForgejo / Plane / Kuma DB]
LocalEnv["local/.env\n(PC only)"]
AgeKey["secrets/.age/key.txt\n(private age key)"]
WPSecrets[Woodpecker repo secrets\nin Woodpecker DB]
end
subgraph encrypted_git [In git — encrypted]
VPSEnc["secrets/vps-stack.enc.env"]
BridgeEnc["secrets/plane-bridge.enc.env"]
MCPEnc["secrets/plane-mcp.enc.env"]
ProjEnc["secrets/projects/*.enc.env"]
end
subgraph vps_runtime [VPS — runtime only]
Tmpfs["/run/infra-secrets/\n(tmpfs RAM)"]
DockerEnv[Container environment\nin memory]
end
AgeKey -->|decrypt| VPSEnc
AgeKey -->|decrypt| BridgeEnc
WPSecrets -->|CI pipeline| CI[Woodpecker agent]
VPSEnc --> Tmpfs
BridgeEnc --> Tmpfs
Tmpfs --> DockerEnv
LocalEnv --> DevScripts[Scripts on your PC]
| Location | What goes here | Committed? | On VPS disk? |
|---|---|---|---|
infra-devops/local/.env |
PLANE_API_KEY, FORGEJO_API_TOKEN, KUMA_PASSWORD, server IP |
No (gitignored) | No |
infra-devops/local/projects/portfolio.env |
SFTP host/user/pass for onboard script | No | No |
secrets/*.enc.env |
Platform OAuth, DB passwords, bridge tokens | Yes (encrypted with SOPS + age) | Encrypted copy in /opt/devops/secrets/ |
secrets/.age/key.txt |
Private age key to decrypt SOPS files | Never | Never on VPS |
| Woodpecker → repo → Secrets | sftp_*, plane_api_key, deploy_ssh_key, sops_age_key |
No | Stored in Woodpecker Postgres only |
| Forgejo / Plane / Kuma DB | User password hashes | N/A | Yes (app databases) |
/opt/devops/.env |
— | — | Should not exist after SOPS migration |
/run/infra-secrets/ |
Decrypted keys during deploy | — | tmpfs (RAM, cleared on reboot) |
Design goal: no long-lived plain password files on the VPS. Platform secrets decrypt at deploy into RAM, then Docker containers get env vars in memory.
Woodpecker secrets (per repository)¶
Woodpecker injects secrets into pipeline containers as environment variables. Developers usually cannot read secret values in the UI after save (only update them).
infra/devops repo (platform auto-deploy)¶
| Secret name | Purpose | Who sets it |
|---|---|---|
sops_age_key |
Decrypt secrets/*.enc.env during deploy |
Rafael (one line from secrets/.age/key.txt) |
deploy_ssh_key |
SSH to VPS for rsync + deploy-platform.sh |
Rafael (base64 of ~/.ssh/bioscan_vps) |
Host (85.215.32.166) and user (root) are hardcoded in .woodpecker.yml — not secrets.
rafael.gonzalez.albes/portfolio repo (site deploy)¶
| Secret name | Purpose |
|---|---|
sftp_host |
IONOS hostname |
sftp_user |
SFTP username |
sftp_pass |
SFTP password |
sftp_remote_path |
Remote folder (e.g. dist) |
plane_api_key |
Post CI status comments to Plane |
plane_workspace |
e.g. rafael-gonzalez-albes |
plane_project_id |
Portfolio Plane project UUID |
plane_api_url |
https://pm.rafaelgonzalezalbes.com/api/v1 |
plane_issue_prefix |
PORT |
woodpecker_host |
https://ci.rafaelgonzalezalbes.com (optional) |
Set via bash scripts/onboard-project.sh or Woodpecker UI.
Future bioscan/bioscancheck¶
Will add: ssh_host, ssh_user, ssh_key, docker_registry_*, SFTP secrets — see BioScanCheck.
SOPS encrypted files (platform)¶
Edited on Rafael’s PC only (needs age private key):
export SOPS_AGE_KEY_FILE=secrets/.age/key.txt
sops edit secrets/plane-bridge.enc.env
| File | Contents |
|---|---|
secrets/vps-stack.enc.env |
WOODPECKER_DB_PASSWORD, WOODPECKER_AGENT_SECRET, WOODPECKER_FORGEJO_CLIENT, WOODPECKER_FORGEJO_SECRET, TRAEFIK_DASHBOARD_AUTH, DOMAIN, admin users |
secrets/plane-bridge.enc.env |
PFB_FORGE_TOKEN, PFB_FORGE_WEBHOOK_SECRET, PFB_PLANE_API_KEY, PFB_PLANE_WEBHOOK_SECRET |
secrets/plane-mcp.enc.env |
Plane MCP OAuth client ID/secret |
secrets/projects/portfolio.enc.env |
Optional encrypted backup of SFTP creds (reference; Woodpecker is source of truth for CI) |
After git push to infra/devops main, Woodpecker deploys and deploy-platform.sh decrypts these into /run/infra-secrets/.
How services connect to each other¶
Big picture¶
flowchart TB
subgraph internet [Internet]
Users[Rafael / Alex browsers]
Public[rafaelgonzalezalbes.com visitors]
IONOS[IONOS SFTP hosting]
end
subgraph vps [VPS 85.215.32.166]
T[Traefik :443]
F[Forgejo git.*]
W[Woodpecker ci.*]
P[Plane pm.*]
B[plane-forge-bridge bridge.*]
K[Uptime Kuma status.*]
D[Docs nginx docs.*]
M[Plane MCP mcp.*]
end
Users --> T
T --> F & W & P & K & D & M
Public --> IONOS
Users -->|OAuth login| W
W -->|OAuth token| F
W -->|clone / API| F
W -->|SFTP deploy secrets| IONOS
W -->|SSH + SOPS deploy| vps
W -->|notify-plane-ci.sh| P
F -->|webhook + HMAC secret| B
P -->|webhook + HMAC secret| B
B -->|Forgejo API + PAT| F
B -->|Plane API + PAT| P
K -->|HTTP checks| F & W & P & D & IONOS
Connection table¶
| From | To | Protocol | Authentication |
|---|---|---|---|
| Browser | Any *.rafaelgonzalezalbes.com subdomain |
HTTPS | TLS (Let’s Encrypt via Traefik) |
| Browser | Forgejo | HTTPS + session cookie | Username + password |
| Browser | Woodpecker | HTTPS → redirect to Forgejo OAuth | Forgejo OAuth app (confidential client) |
| Browser | Plane | HTTPS + session | Email + password (invite only) |
| Browser | Plane MCP (mcp.*) |
HTTPS | Plane OAuth |
| Woodpecker agent | Forgejo | HTTPS git clone | User OAuth token (stored in Woodpecker DB after login) |
| Woodpecker agent | IONOS | SFTP | Repo secrets sftp_* |
| Woodpecker agent | VPS | SSH | deploy_ssh_key secret |
| Woodpecker agent | Plane REST API | HTTPS | plane_api_key secret in notify script |
| Forgejo | plane-forge-bridge | HTTPS webhook POST | PFB_FORGE_WEBHOOK_SECRET (HMAC) |
| Plane | plane-forge-bridge | HTTPS webhook POST | PFB_PLANE_WEBHOOK_SECRET (HMAC) |
| plane-forge-bridge | Forgejo API | HTTPS | PFB_FORGE_TOKEN (PAT) |
| plane-forge-bridge | Plane API | HTTPS | PFB_PLANE_API_KEY (PAT) |
| Uptime Kuma | All services | HTTP(S) probes | None (internal checks) |
| Cursor (local) | Plane MCP | HTTP | OAuth |
| Cursor (local) | Forgejo / Woodpecker | stdio MCP | PAT in local/.env |
plane-forge-bridge (Forgejo ↔ Plane)¶
Runs at https://bridge.rafaelgonzalezalbes.com. Not for browsing — webhooks only.
Linked today:
| Forgejo repo | Plane project | Prefix |
|---|---|---|
rafael.gonzalez.albes/portfolio |
Portfolio | PORT |
Config: plane/bridge/config.yaml in infra/devops.
What it syncs:
- Forgejo issue open/close → Plane state (e.g. PR opened → In Review)
- Plane work item events → Forgejo (when workspace webhook configured)
- PR merge → Done
One-time manual step (Rafael): Plane → Workspace Settings → Webhooks → URL https://bridge.rafaelgonzalezalbes.com/plane/webhook, secret = PFB_PLANE_WEBHOOK_SECRET from SOPS.
Woodpecker → Plane (CI comments)¶
On every pipeline, scripts/notify-plane-ci.sh runs if the branch or commit message contains PORT-N (or BIOS-N):
- Parses issue key from
CI_COMMIT_BRANCH/ message - Calls Plane API: add comment with CI status + link to Woodpecker build
- Uses Woodpecker repo secrets (
plane_api_key, etc.)
No link if branch doesn’t contain PORT-12 style key.
Data flows by project¶
Portfolio (live today)¶
sequenceDiagram
participant Dev as Developer
participant F as Forgejo
participant W as Woodpecker
participant I as IONOS SFTP
participant Site as rafaelgonzalezalbes.com
participant P as Plane
Dev->>F: push feature/PORT-5-...
F->>W: webhook trigger pipeline
W->>F: clone repo (OAuth token)
W->>W: npm ci, lint, build
W->>P: notify success/failure (plane_api_key)
Dev->>F: merge PR to main
F->>W: pipeline on main
W->>I: SFTP upload dist/ (sftp_* secrets)
I->>Site: static files served
Important: Git repo is private on Forgejo. Public website is separate on IONOS — locking the repo does not take down the site.
Platform infra/devops¶
sequenceDiagram
participant Dev as Rafael
participant F as Forgejo
participant W as Woodpecker
participant VPS as VPS /opt/devops
Dev->>F: push main
F->>W: trigger pipeline
W->>W: mkdocs build → site/
W->>VPS: rsync repo + site/ (SSH)
W->>VPS: deploy-platform.sh (SOPS_AGE_KEY)
VPS->>VPS: decrypt secrets → tmpfs
VPS->>VPS: docker compose up -d
BioScanCheck (planned)¶
Frontend → IONOS SFTP. Backend MCP Docker image → Forgejo registry → SSH to VPS. See apps/bioscancheck.md.
What Alex has vs what only Rafael has¶
| Secret / access | Alex | Rafael |
|---|---|---|
| Forgejo login password | Own password | Own password |
| Plane login | Own invite | Admin |
| Woodpecker (via Forgejo OAuth) | Yes | Yes |
Forgejo org admin (infra, bioscan) |
Yes | Yes |
| Woodpecker repo secrets (view/edit) | Admin — can update | Admin |
local/.env on PC |
His own if he sets up automation | Yes |
secrets/.age/key.txt |
No (unless explicitly shared) | Yes |
Woodpecker sops_age_key |
No | Yes |
| IONOS SFTP password | No (unless shared) | Yes (in Woodpecker) |
| VPS root SSH key | Optional alex.rodriguez.vera user |
Yes |
Alex can develop, push, review PRs, and trigger CI without knowing SFTP or SOPS keys — Woodpecker already has them.
Rules for both of you¶
- Never commit
local/.env,local/projects/*.env,secrets/.age/, or any file with passwords. - Never paste live secrets in chat, tickets, or PRs.
- Rotate tokens if they were ever exposed — see
scripts/rotate-exposed-secrets.md. - SFTP / SSH / age key — ask Rafael to update Woodpecker secrets, don’t embed in code.
- Forgejo PAT for MCP — generate your own under Settings → Applications.
- Platform URLs use
X-Robots-Tag: noindex— not in Google, but still use HTTPS and strong passwords.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Woodpecker clone fails (exit 128) | Repo private but Woodpecker repos.private=false |
bash scripts/fix-portfolio-repo-private.sh + refresh OAuth |
| Woodpecker “Sign in with Forgejo” fails | OAuth app misconfigured | Confidential client required — see scripts/setup-woodpecker-oauth.sh |
| No Plane comment on CI | Branch missing PORT-N |
Rename branch to feature/PORT-12-... |
| No Plane comment on CI | Missing Woodpecker plane secrets | Re-run onboard-project.sh |
infra/devops deploy fails |
Bad deploy_ssh_key paste |
Use base64 one-liner in Woodpecker |
docs.* 404 or 403 |
Traefik middleware or file permissions | See deploy runbook; chmod -R a+rX /opt/devops/site |
| Bridge not syncing PRs | Webhook missing in Forgejo/Plane | Check plane/bridge/config.yaml + workspace webhook |
Resumen en español¶
Dos tipos de credenciales¶
- Contraseñas de usuario (Forgejo, Plane, Kuma): las guarda cada aplicación en su base de datos. No están en git.
- Secretos de máquina (CI, webhooks, deploy): nunca en el código.
Dónde viven los secretos¶
| Lugar | Qué hay |
|---|---|
| Woodpecker (por repo) | SFTP, SSH deploy, API de Plane para comentarios CI |
secrets/*.enc.env (git cifrado) |
OAuth Woodpecker, tokens del bridge Plane↔Forgejo, contraseñas de BD |
local/.env (solo PC) |
Tokens para scripts y MCP de Cursor |
secrets/.age/key.txt |
Clave privada para descifrar SOPS — solo Rafael, nunca en git |
Cómo se conectan las apps¶
- Traefik enruta todos los subdominios (
git,ci,pm,docs,status,mcp,bridge). - Woodpecker entra con OAuth de Forgejo, clona repos, despliega portfolio por SFTP a IONOS, y la plataforma por SSH al VPS.
- plane-forge-bridge sincroniza PRs/issues entre Forgejo y Plane con webhooks y tokens API.
- Woodpecker avisa a Plane con
notify-plane-ci.shsi la rama contienePORT-N. - Cursor habla con Plane vía MCP OAuth; con Forgejo/Woodpecker vía tokens personales en el PC.
Qué necesita Alex de Rafael¶
- Invitación a Plane y contraseña de Kuma (si aplica).
- No necesita la clave age ni SFTP para trabajar en código — el CI ya tiene esos secretos en Woodpecker.
Related pages¶
- Architecture → Secrets — SOPS edit, rotate age key, backup rules
- Access control — who can see what
- Team onboarding — first steps for Alex
- Onboarding (español)
- Architecture overview — DNS and CI flows