Read TELEGRAM_CHAT_ID from OpenBao in the five notify jobs that still use a repository secret #88

Open
opened 2026-09-12 20:16:22 +00:00 by ahmad · 7 comments
Owner

Context

Five repositories' notify jobs read TELEGRAM_CHAT_ID from a Forgejo repository secret (${{ secrets.TELEGRAM_CHAT_ID }}): ahmad/larda, ahmad/expiro, bayan/bayan-web, amtronics/forgejo-mcp, amtronics/agent-bus. The other ten fetch it from OpenBao with openbao-fetch, so a change in apps/<app>/secrets.vault.yml reaches them on the next run. For the five, nothing writes the secret: the alerts/CI-CD split (infrastructure-vps#449) had to set them by hand.

Scope

  • In: the five notify jobs (release and deploy workflows) read TELEGRAM_CHAT_ID from the fetched secrets like the rest; the hand-set repository secrets removed once each is merged.
  • Out: the bot token (an org secret, unchanged).

Acceptance criteria

  • Given a change to TELEGRAM_CHAT_ID in an app's vault file, when the sync has run, then that app's next pipeline notification uses the new value with no hand step.
  • Given the five repositories, then none carries a TELEGRAM_CHAT_ID repository secret.
### Context Five repositories' notify jobs read `TELEGRAM_CHAT_ID` from a Forgejo repository secret (`${{ secrets.TELEGRAM_CHAT_ID }}`): `ahmad/larda`, `ahmad/expiro`, `bayan/bayan-web`, `amtronics/forgejo-mcp`, `amtronics/agent-bus`. The other ten fetch it from OpenBao with `openbao-fetch`, so a change in `apps/<app>/secrets.vault.yml` reaches them on the next run. For the five, nothing writes the secret: the alerts/CI-CD split (infrastructure-vps#449) had to set them by hand. ### Scope - In: the five notify jobs (release and deploy workflows) read `TELEGRAM_CHAT_ID` from the fetched secrets like the rest; the hand-set repository secrets removed once each is merged. - Out: the bot token (an org secret, unchanged). ### Acceptance criteria - [ ] Given a change to `TELEGRAM_CHAT_ID` in an app's vault file, when the sync has run, then that app's next pipeline notification uses the new value with no hand step. - [ ] Given the five repositories, then none carries a `TELEGRAM_CHAT_ID` repository secret.
ahmad self-assigned this 2026-09-13 05:47:52 +00:00
Author
Owner

Plan, and a correction to the scope

It is fifteen workflow files, not five notify jobs. Each of the five repositories reads secrets.TELEGRAM_CHAT_ID in three workflows, not the two the scope names — pr.yml as well as release.yml and deploy.yml. Criterion 2 says no repository carries the secret, and that cannot be true while a pr.yml still reads it, so all three move or the secret stays. Counted, not assumed:

ahmad/larda          pr.yml release.yml deploy.yml
ahmad/expiro         pr.yml release.yml deploy.yml
bayan/bayan-web      pr.yml release.yml deploy.yml
amtronics/forgejo-mcp  pr.yml release.yml deploy.yml
amtronics/agent-bus    pr.yml release.yml deploy.yml

One pull request per repository, three files each — that keeps every change inside the size this estate takes in one PR, and a repository whose pipeline breaks is isolated to its own change. Starting with ahmad/larda.

The pattern is ahmad/trip's, copied rather than invented: an openbao-fetch step with required: false, then a step that reads the key out of the rendered .env, masks it, writes it to GITHUB_ENV and deletes the file. required: false is the important half and it is already reasoned out in trip's own comment — a notification is worth sending and not worth reddening the run it exists to report on.

Two things I checked rather than assumed:

  • Every one of the five already has OPENBAO_ROLE_ID and OPENBAO_SECRET_ID as repository secrets, so none needs a secret created to do this.
  • bayan/bayan-web has no openbao-fetch anywhere and no OPENBAO_ADDR / OPENBAO_PATH, so its change adds the workflow env as well as the steps. The other four already fetch from OpenBao in release.yml and deploy.yml; in all five, pr.yml has no OpenBao wiring at all and gains the env too.

The bot token stays as it is, per the scope. In larda that means the fetch takes TELEGRAM_CHAT_ID only and token: keeps reading the repository secret.

I am not deleting the repository secrets. Criterion 2's second half is a remote mutation on secret storage that cannot be undone from a repository, and it is only safe once a repository's replacement has actually run green. Once all five are merged I will report which are ready to remove and leave the removal to you, under Needs you.

One observation, out of scope and not touched: ahmad/expiro's notify jobs read secrets.TELEGRAM_BOT_TOKEN, expiro has no such repository secret, and ahmad is a user rather than an organisation, so there is no org secret behind it either. If that resolves empty then expiro's pipeline notifications are being skipped silently — notify-telegram treats an empty token as a skip. I could not confirm it (the user-secrets endpoint answers 404 on this instance), so it is worth a look rather than a claim, and it is a separate issue from this one either way.

### Plan, and a correction to the scope **It is fifteen workflow files, not five notify jobs.** Each of the five repositories reads `secrets.TELEGRAM_CHAT_ID` in **three** workflows, not the two the scope names — `pr.yml` as well as `release.yml` and `deploy.yml`. Criterion 2 says no repository carries the secret, and that cannot be true while a `pr.yml` still reads it, so all three move or the secret stays. Counted, not assumed: ``` ahmad/larda pr.yml release.yml deploy.yml ahmad/expiro pr.yml release.yml deploy.yml bayan/bayan-web pr.yml release.yml deploy.yml amtronics/forgejo-mcp pr.yml release.yml deploy.yml amtronics/agent-bus pr.yml release.yml deploy.yml ``` **One pull request per repository**, three files each — that keeps every change inside the size this estate takes in one PR, and a repository whose pipeline breaks is isolated to its own change. Starting with `ahmad/larda`. **The pattern is `ahmad/trip`'s, copied rather than invented:** an `openbao-fetch` step with `required: false`, then a step that reads the key out of the rendered `.env`, masks it, writes it to `GITHUB_ENV` and deletes the file. `required: false` is the important half and it is already reasoned out in `trip`'s own comment — a notification is worth sending and not worth reddening the run it exists to report on. **Two things I checked rather than assumed:** - Every one of the five already has `OPENBAO_ROLE_ID` and `OPENBAO_SECRET_ID` as repository secrets, so none needs a secret created to do this. - `bayan/bayan-web` has **no** `openbao-fetch` anywhere and no `OPENBAO_ADDR` / `OPENBAO_PATH`, so its change adds the workflow env as well as the steps. The other four already fetch from OpenBao in `release.yml` and `deploy.yml`; in all five, `pr.yml` has no OpenBao wiring at all and gains the env too. **The bot token stays as it is**, per the scope. In `larda` that means the fetch takes `TELEGRAM_CHAT_ID` only and `token:` keeps reading the repository secret. **I am not deleting the repository secrets.** Criterion 2's second half is a remote mutation on secret storage that cannot be undone from a repository, and it is only safe once a repository's replacement has actually run green. Once all five are merged I will report which are ready to remove and leave the removal to you, under **Needs you**. **One observation, out of scope and not touched:** `ahmad/expiro`'s notify jobs read `secrets.TELEGRAM_BOT_TOKEN`, expiro has no such repository secret, and `ahmad` is a user rather than an organisation, so there is no org secret behind it either. If that resolves empty then expiro's pipeline notifications are being skipped silently — `notify-telegram` treats an empty token as a skip. I could not confirm it (the user-secrets endpoint answers 404 on this instance), so it is worth a look rather than a claim, and it is a separate issue from this one either way.
Author
Owner

First repository done, and one question that gates the other four

ahmad/larda: ahmad/larda#206 — all three notify jobs fetch the chat id from OpenBao, no path left reading the repository secret, pipeline green, docs/CONFIGURATION.md moved in the same change. Left as WIP: on purpose.

The gap. A green notify does not prove the notification was sent: required: false plus an empty value is a clean skip, and a skip succeeds. I could not close it from here — this Forgejo exposes no Actions log endpoint to a token (every /logs and artifacts shape answers 404), and the app vaults are encrypted under a vault id whose password the workspace does not hold, so I could not read the key's presence either. My first attempt at that reported the key missing from all seven vaults I checked, which was a suppressed Decryption failed rather than an answer — trip and portfolio fetch this key today, so a result saying it is absent from their vaults refuted itself.

One glance settles it for all five: did the CI/CD chat receive a CI notification for ahmad/larda#206?

  • Yes — the vault carries the key, the pattern is sound, and I take #206 out of WIP: and do the remaining four the same way.
  • No — the key needs adding to the five apps' vault files first, which is an infrastructure-vps change and out of this loop's scope. Nothing should merge before that, or notifications stop silently in five repositories at once.

The alternative, if you would rather not be the check: chat-id: ${{ env.TELEGRAM_CHAT_ID || secrets.TELEGRAM_CHAT_ID }} in all five makes the change strictly non-regressive — vault first, existing secret as the fallback — and then criterion 2's deletions happen later, once something has actually proven the vault value arrives. It costs keeping the secret a while longer and reads slightly worse; it cannot regress anything.

Remaining: ahmad/expiro, bayan/bayan-web, amtronics/forgejo-mcp, amtronics/agent-bus — three workflow files each, bayan-web also needing the OPENBAO_ADDR / OPENBAO_PATH env it has nowhere today.

### First repository done, and one question that gates the other four **`ahmad/larda`: https://git.ahmadelmasri.com/ahmad/larda/pulls/206** — all three notify jobs fetch the chat id from OpenBao, no path left reading the repository secret, pipeline green, `docs/CONFIGURATION.md` moved in the same change. **Left as `WIP:` on purpose.** **The gap.** A green `notify` does not prove the notification was sent: `required: false` plus an empty value is a clean skip, and a skip succeeds. I could not close it from here — this Forgejo exposes no Actions log endpoint to a token (every `/logs` and artifacts shape answers 404), and the app vaults are encrypted under a vault id whose password the workspace does not hold, so I could not read the key's presence either. My first attempt at that reported the key missing from all seven vaults I checked, which was a suppressed `Decryption failed` rather than an answer — `trip` and `portfolio` fetch this key today, so a result saying it is absent from their vaults refuted itself. **One glance settles it for all five:** did the CI/CD chat receive a `CI` notification for `ahmad/larda#206`? - **Yes** — the vault carries the key, the pattern is sound, and I take `#206` out of `WIP:` and do the remaining four the same way. - **No** — the key needs adding to the five apps' vault files first, which is an `infrastructure-vps` change and out of this loop's scope. Nothing should merge before that, or notifications stop silently in five repositories at once. The alternative, if you would rather not be the check: `chat-id: ${{ env.TELEGRAM_CHAT_ID || secrets.TELEGRAM_CHAT_ID }}` in all five makes the change strictly non-regressive — vault first, existing secret as the fallback — and then criterion 2's deletions happen later, once something has actually proven the vault value arrives. It costs keeping the secret a while longer and reads slightly worse; it cannot regress anything. **Remaining: `ahmad/expiro`, `bayan/bayan-web`, `amtronics/forgejo-mcp`, `amtronics/agent-bus`** — three workflow files each, `bayan-web` also needing the `OPENBAO_ADDR` / `OPENBAO_PATH` env it has nowhere today.
Author
Owner

The expiro observation from my plan comment is now verified and filed as ahmad/expiro#96. I raised it here as "worth a look rather than a claim" and then did not file it, which was the mistake; this is the correction.

It is real: expiro's three notify jobs send with secrets.TELEGRAM_BOT_TOKEN, that secret is not among its repository secrets, and ahmad is a user namespace rather than an organisation (GET /orgs/ahmad → 404), so there is nothing to inherit from. An empty token is a clean skip inside notify-telegram, and a skip succeeds — so the job has been green while sending nothing.

It is expiro alone, which I checked rather than assumed: larda and namey also read secrets. and both hold the secret; trip, portfolio, imamah and launchpad fetch theirs from OpenBao.

It touches this issue at one point. The clean fix is to fetch the token from OpenBao the way four sibling repositories already do — which depends on secret/expiro carrying it, the same unverified question that holds this issue. So whichever way the vault question is answered here, it answers that too. The bot token stays out of scope for this issue; the dependency is recorded on #96 rather than folded in.

**The expiro observation from my plan comment is now verified and filed as `ahmad/expiro#96`.** I raised it here as "worth a look rather than a claim" and then did not file it, which was the mistake; this is the correction. It is real: expiro's three notify jobs send with `secrets.TELEGRAM_BOT_TOKEN`, that secret is not among its repository secrets, and `ahmad` is a user namespace rather than an organisation (`GET /orgs/ahmad` → 404), so there is nothing to inherit from. An empty token is a clean skip inside `notify-telegram`, and a skip succeeds — so the job has been green while sending nothing. **It is expiro alone**, which I checked rather than assumed: `larda` and `namey` also read `secrets.` and both hold the secret; `trip`, `portfolio`, `imamah` and `launchpad` fetch theirs from OpenBao. **It touches this issue at one point.** The clean fix is to fetch the token from OpenBao the way four sibling repositories already do — which depends on `secret/expiro` carrying it, the same unverified question that holds this issue. So whichever way the vault question is answered here, it answers that too. The bot token stays out of scope for this issue; the dependency is recorded on `#96` rather than folded in.
Author
Owner

Correcting my own scope above: it is four repositories, not five

bayan/bayan-web is already done and should come out of this issue. All four of its workflows that call notify-telegram — pr, release, deploy, monitoring — already fetch the value from OpenBao and read env.TELEGRAM_CHAT_ID. It was converted in its own Bayan/bayan-web#32, "ci: Let a vault outage skip the notification".

Why I said otherwise. My plan comment counted "fifteen workflow files across five repositories". I measured that by grepping the local main-wt worktrees — and bayan-web's is 11 commits behind its remote, so I read the version from before the conversion. Measured again against each repository's actual head, verified equal to ls-remote in every case:

ahmad/larda            3 files   (PR #206 open, held on the question above)
ahmad/expiro           3 files
amtronics/forgejo-mcp  3 files
amtronics/agent-bus    3 files
bayan/bayan-web        0 files   — already converted, in #32

So: twelve files across four repositories, one of which already has its pull request open.

bayan-web#32 is the precedent worth copying rather than re-deriving. It is the same change, already reviewed and merged in this estate, and its title records the same conclusion I reached independently on #206 — a vault outage must skip the notification rather than fail the run.

One trap for anyone repeating this measurement. amtronics/agent-bus's bare clone has no refs/remotes/origin/* at all — its fetch lands in FETCH_HEAD and the ref to read is main. A sweep keyed on origin/main reports zero matches for it and looks like a clean result, which is how it dropped out of my first recount. Its local main is current with the remote and it does read secrets.TELEGRAM_CHAT_ID in three files, so it stays in scope.

Nothing about the approach changes — only the count, the list, and where to look for the precedent.

### Correcting my own scope above: it is four repositories, not five **`bayan/bayan-web` is already done** and should come out of this issue. All four of its workflows that call `notify-telegram` — `pr`, `release`, `deploy`, `monitoring` — already fetch the value from OpenBao and read `env.TELEGRAM_CHAT_ID`. It was converted in its own `Bayan/bayan-web#32`, *"ci: Let a vault outage skip the notification"*. **Why I said otherwise.** My plan comment counted "fifteen workflow files across five repositories". I measured that by grepping the local `main-wt` worktrees — and `bayan-web`'s is **11 commits behind** its remote, so I read the version from before the conversion. Measured again against each repository's actual head, verified equal to `ls-remote` in every case: ``` ahmad/larda 3 files (PR #206 open, held on the question above) ahmad/expiro 3 files amtronics/forgejo-mcp 3 files amtronics/agent-bus 3 files bayan/bayan-web 0 files — already converted, in #32 ``` So: **twelve files across four repositories**, one of which already has its pull request open. **`bayan-web#32` is the precedent worth copying** rather than re-deriving. It is the same change, already reviewed and merged in this estate, and its title records the same conclusion I reached independently on `#206` — a vault outage must skip the notification rather than fail the run. **One trap for anyone repeating this measurement.** `amtronics/agent-bus`'s bare clone has no `refs/remotes/origin/*` at all — its fetch lands in `FETCH_HEAD` and the ref to read is `main`. A sweep keyed on `origin/main` reports **zero matches** for it and looks like a clean result, which is how it dropped out of my first recount. Its local `main` is current with the remote and it does read `secrets.TELEGRAM_CHAT_ID` in three files, so it stays in scope. Nothing about the approach changes — only the count, the list, and where to look for the precedent.
Author
Owner

Resuming: the remaining three repositories

ahmad/larda#206 merged on 2026-09-18 with the vault value proven from its own notify job log, so the pattern is settled and the same change goes to ahmad/expiro, amtronics/forgejo-mcp and amtronics/agent-bus next — one pull request each, WIP: until that repository's own notify log shows the chat id arrived from the vault. The three changes are prepared; the pull requests follow.

Same shape as #206 in every file: openbao-fetch with required: false, a masked load into the job env, notify-telegram reading env.TELEGRAM_CHAT_ID; pr.yml gains the OPENBAO_ADDR / OPENBAO_PATH env the other two workflows already declare; docs/CONFIGURATION.md moves the row. The bot token stays as it is.

One thing the docs had wrong on the two amtronics repositories: their CONFIGURATION.md lists TELEGRAM_CHAT_ID as a Forgejo org secret, but the org holds only TELEGRAM_BOT_TOKEN and the chat id is a repository secret in each (read back from the secrets endpoints). The moved row corrects that as a side effect.

ahmad/expiro's notification still cannot be observed delivered from here — ahmad/expiro#96 (no bot token) is what stops it, not this change — so for that one the log can only show the fetch succeeding, and that is what will be reported.

### Resuming: the remaining three repositories `ahmad/larda#206` merged on 2026-09-18 with the vault value proven from its own notify job log, so the pattern is settled and the same change goes to `ahmad/expiro`, `amtronics/forgejo-mcp` and `amtronics/agent-bus` next — one pull request each, `WIP:` until that repository's own notify log shows the chat id arrived from the vault. The three changes are prepared; the pull requests follow. Same shape as `#206` in every file: `openbao-fetch` with `required: false`, a masked load into the job env, `notify-telegram` reading `env.TELEGRAM_CHAT_ID`; `pr.yml` gains the `OPENBAO_ADDR` / `OPENBAO_PATH` env the other two workflows already declare; `docs/CONFIGURATION.md` moves the row. The bot token stays as it is. One thing the docs had wrong on the two `amtronics` repositories: their `CONFIGURATION.md` lists `TELEGRAM_CHAT_ID` as a Forgejo **org** secret, but the org holds only `TELEGRAM_BOT_TOKEN` and the chat id is a **repository** secret in each (read back from the secrets endpoints). The moved row corrects that as a side effect. `ahmad/expiro`'s notification still cannot be observed delivered from here — `ahmad/expiro#96` (no bot token) is what stops it, not this change — so for that one the log can only show the fetch succeeding, and that is what will be reported.
Author
Owner

The last three consumers now have a WIP PR: ahmad/expiro#99, amtronics/forgejo-mcp#21, amtronics/agent-bus#17. Each will be marked ready once its own notify job shows the vault fetch and a delivered notification (expiro: fetch only, see expiro#96).

The last three consumers now have a WIP PR: ahmad/expiro#99, amtronics/forgejo-mcp#21, amtronics/agent-bus#17. Each will be marked ready once its own notify job shows the vault fetch and a delivered notification (expiro: fetch only, see expiro#96).
Author
Owner

All four consumers merged — one hand step left, yours

Merged on the bot's approval with green checks: ahmad/expiro#99 (df13d75), amtronics/forgejo-mcp#21 (d3e513b), amtronics/agent-bus#17 (2219776); ahmad/larda#206 earlier, and Bayan/bayan-web was already done (#32).

  • Criterion 1. Each PR's own notify job logged openbao-fetch: wrote … from secret/<app>; forgejo-mcp and agent-bus then Telegram notification delivered. (expiro skips sending: it has no bot token, ahmad/expiro#96).
  • Criterion 2 — needs a human. No workflow on main in any of the five reads secrets.TELEGRAM_CHAT_ID any more (grepped each origin/main), but the repository secret still exists in all five: ahmad/larda, ahmad/expiro, Bayan/bayan-web, amtronics/forgejo-mcp, amtronics/agent-bus. Deleting secrets is outside what the loop may do. Safe to delete now; bayan-web's NOTIFY_TELEGRAM_* pair is a separate question, not touched.

Moving this to state/blocked on that step; close it once the five are gone.

### All four consumers merged — one hand step left, yours Merged on the bot's approval with green checks: `ahmad/expiro#99` (df13d75), `amtronics/forgejo-mcp#21` (d3e513b), `amtronics/agent-bus#17` (2219776); `ahmad/larda#206` earlier, and `Bayan/bayan-web` was already done (#32). - [x] **Criterion 1.** Each PR's own notify job logged `openbao-fetch: wrote … from secret/<app>`; forgejo-mcp and agent-bus then `Telegram notification delivered.` (expiro skips sending: it has no bot token, `ahmad/expiro#96`). - [ ] **Criterion 2 — needs a human.** No workflow on `main` in any of the five reads `secrets.TELEGRAM_CHAT_ID` any more (grepped each `origin/main`), but the repository secret still exists in all five: `ahmad/larda`, `ahmad/expiro`, `Bayan/bayan-web`, `amtronics/forgejo-mcp`, `amtronics/agent-bus`. Deleting secrets is outside what the loop may do. Safe to delete now; bayan-web's `NOTIFY_TELEGRAM_*` pair is a separate question, not touched. Moving this to `state/blocked` on that step; close it once the five are gone.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
amtronics/platform-actions#88
No description provided.