feat: provision app secrets in notion-mcp-remote namespace (pre-sync) #7

Open
opened 2026-04-21 01:30:46 +00:00 by forgejo_admin · 3 comments
Contributor

Type

Feature

Lineage

Standalone — scoped from project-notion-mcp-remote. Follows service-onboarding-sop → "Application secrets" row of the Pre-Deploy Validation Checklist.

Repo

forgejo_admin/notion-mcp-remote

User Story

As an operator
I want the notion-mcp-secrets Kubernetes Secret pre-created in the target namespace
So that ArgoCD's first sync produces a running pod instead of CreateContainerConfigError.

Context

Per SOP: kustomize overlays must not contain real secret data. The merged k8s/deployment.yaml on main references the Kubernetes Secret notion-mcp-secrets (not notion-mcp-remote) via valueFrom: secretKeyRef with kebab-case keys. Real secret values must be materialised via kubectl create secret generic before the first ArgoCD sync (or before tofu apply for the initial bring-up if ArgoCD auto-syncs post-create).

Secret shape is dictated by the existing manifest — do not invent new keys. The deployment spec maps each kebab-case secret key to an UPPER_SNAKE_CASE environment variable consumed by server.py / auth/:

Secret key (kebab-case) Env var in pod Consumer
base-url BASE_URL server.py — OAuth redirect + allowed-hosts
oauth-client-id NOTION_OAUTH_CLIENT_ID auth/provider.py — Notion OAuth
oauth-client-secret NOTION_OAUTH_CLIENT_SECRET auth/provider.py — Notion OAuth
session-secret SESSION_SECRET auth/storage.py TokenStore — token encryption
onboard-secret ONBOARD_SECRET onboarding endpoint auth

Depends on feat: register public Notion OAuth integration (provides client id/secret) and the namespace being created by tofu apply on pal-e-platform.

File Targets

No repo file changes. This is a kubectl operation executed against the live cluster. The target Secret is consumed by k8s/deployment.yaml (already on main) — this ticket does not modify that file.

Acceptance Criteria

  • Namespace notion-mcp-remote exists
  • Secret created via kubectl create secret generic notion-mcp-secrets -n notion-mcp-remote with these kebab-case keys:
    • base-url = https://notion-mcp-remote.tail5b443a.ts.net
    • oauth-client-id (from registered Notion integration)
    • oauth-client-secret (from registered Notion integration)
    • session-secret (generated via openssl rand -hex 32)
    • onboard-secret (generated via openssl rand -hex 32)
  • kubectl get secret notion-mcp-secrets -n notion-mcp-remote -o jsonpath='{.data}' | jq 'keys' returns all 5 kebab-case keys
  • Pod actually starts and reads the values: kubectl rollout status deploy/notion-mcp-remote -n notion-mcp-remote reports successfully rolled out (no CreateContainerConfigError, no CrashLoopBackOff)
  • kubectl exec + env (or logs) confirms BASE_URL, NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_CLIENT_SECRET, SESSION_SECRET, ONBOARD_SECRET are all set in the running container
  • curl against the pod's /health endpoint returns 200 (proves the process booted past env-var loading)
  • Secret created before the first ArgoCD sync for this Application
  • Disaster-recovery copy stored in ~/secrets/notion-mcp-remote/

Test Expectations

  • Pod reaches Ready state once image lands (server.py fails loudly on missing required env — green pod == secret shape is correct)
  • kubectl logs -n notion-mcp-remote deploy/notion-mcp-remote shows successful startup banner and no KeyError / missing env traceback
  • Run command: kubectl get secret notion-mcp-secrets -n notion-mcp-remote && kubectl rollout status deploy/notion-mcp-remote -n notion-mcp-remote

Constraints

  • Secret name must be notion-mcp-secrets and keys must be kebab-case — this matches the manifest already on main. Do not "normalise" to UPPER_SNAKE_CASE; the secretKeyRef.key lookups are case-sensitive and would break the pod.
  • Never kubectl apply a YAML Secret manifest from a file that was ever in git
  • session-secret and onboard-secret must be generated at provision time (32+ bytes of entropy), not copy-pasted from anywhere
  • Do not run this until the namespace exists (sibling ticket on pal-e-platform)

Checklist

  • Prereq issues closed (OAuth integration, tofu apply for namespace)
  • Secret created with correct name + kebab-case keys
  • Pod rolls out green (verifies shape matches manifest)
  • DR copy stored locally
  • No unrelated changes
  • project-notion-mcp-remote
  • story-notion-mcp-remote-ops-deploy-gitops
  • service-onboarding-sop
  • arch-deployment-notion-mcp-remote
### Type Feature ### Lineage Standalone — scoped from `project-notion-mcp-remote`. Follows `service-onboarding-sop` → "Application secrets" row of the Pre-Deploy Validation Checklist. ### Repo `forgejo_admin/notion-mcp-remote` ### User Story As an operator I want the `notion-mcp-secrets` Kubernetes Secret pre-created in the target namespace So that ArgoCD's first sync produces a running pod instead of `CreateContainerConfigError`. ### Context Per SOP: kustomize overlays must not contain real secret data. The merged `k8s/deployment.yaml` on main references the Kubernetes Secret **`notion-mcp-secrets`** (not `notion-mcp-remote`) via `valueFrom: secretKeyRef` with **kebab-case keys**. Real secret values must be materialised via `kubectl create secret generic` **before** the first ArgoCD sync (or before `tofu apply` for the initial bring-up if ArgoCD auto-syncs post-create). **Secret shape is dictated by the existing manifest — do not invent new keys.** The deployment spec maps each kebab-case secret key to an UPPER_SNAKE_CASE environment variable consumed by `server.py` / `auth/`: | Secret key (kebab-case) | Env var in pod | Consumer | | --- | --- | --- | | `base-url` | `BASE_URL` | `server.py` — OAuth redirect + allowed-hosts | | `oauth-client-id` | `NOTION_OAUTH_CLIENT_ID` | `auth/provider.py` — Notion OAuth | | `oauth-client-secret` | `NOTION_OAUTH_CLIENT_SECRET` | `auth/provider.py` — Notion OAuth | | `session-secret` | `SESSION_SECRET` | `auth/storage.py` `TokenStore` — token encryption | | `onboard-secret` | `ONBOARD_SECRET` | onboarding endpoint auth | Depends on `feat: register public Notion OAuth integration` (provides client id/secret) and the namespace being created by `tofu apply` on pal-e-platform. ### File Targets No repo file changes. This is a `kubectl` operation executed against the live cluster. The target Secret is consumed by `k8s/deployment.yaml` (already on main) — this ticket does not modify that file. ### Acceptance Criteria - [ ] Namespace `notion-mcp-remote` exists - [ ] Secret created via `kubectl create secret generic notion-mcp-secrets -n notion-mcp-remote` with these **kebab-case** keys: - `base-url` = `https://notion-mcp-remote.tail5b443a.ts.net` - `oauth-client-id` (from registered Notion integration) - `oauth-client-secret` (from registered Notion integration) - `session-secret` (generated via `openssl rand -hex 32`) - `onboard-secret` (generated via `openssl rand -hex 32`) - [ ] `kubectl get secret notion-mcp-secrets -n notion-mcp-remote -o jsonpath='{.data}' | jq 'keys'` returns all 5 kebab-case keys - [ ] **Pod actually starts and reads the values:** `kubectl rollout status deploy/notion-mcp-remote -n notion-mcp-remote` reports `successfully rolled out` (no `CreateContainerConfigError`, no `CrashLoopBackOff`) - [ ] `kubectl exec` + `env` (or logs) confirms `BASE_URL`, `NOTION_OAUTH_CLIENT_ID`, `NOTION_OAUTH_CLIENT_SECRET`, `SESSION_SECRET`, `ONBOARD_SECRET` are all set in the running container - [ ] `curl` against the pod's `/health` endpoint returns 200 (proves the process booted past env-var loading) - [ ] Secret created **before** the first ArgoCD sync for this Application - [ ] Disaster-recovery copy stored in `~/secrets/notion-mcp-remote/` ### Test Expectations - [ ] Pod reaches `Ready` state once image lands (`server.py` fails loudly on missing required env — green pod == secret shape is correct) - [ ] `kubectl logs -n notion-mcp-remote deploy/notion-mcp-remote` shows successful startup banner and no `KeyError` / `missing env` traceback - Run command: `kubectl get secret notion-mcp-secrets -n notion-mcp-remote && kubectl rollout status deploy/notion-mcp-remote -n notion-mcp-remote` ### Constraints - Secret name **must be** `notion-mcp-secrets` and keys **must be** kebab-case — this matches the manifest already on main. Do not "normalise" to UPPER_SNAKE_CASE; the `secretKeyRef.key` lookups are case-sensitive and would break the pod. - Never `kubectl apply` a YAML Secret manifest from a file that was ever in git - `session-secret` and `onboard-secret` must be generated at provision time (32+ bytes of entropy), not copy-pasted from anywhere - Do not run this until the namespace exists (sibling ticket on pal-e-platform) ### Checklist - [ ] Prereq issues closed (OAuth integration, tofu apply for namespace) - [ ] Secret created with correct name + kebab-case keys - [ ] Pod rolls out green (verifies shape matches manifest) - [ ] DR copy stored locally - [ ] No unrelated changes ### Related - `project-notion-mcp-remote` - `story-notion-mcp-remote-ops-deploy-gitops` - `service-onboarding-sop` - `arch-deployment-notion-mcp-remote`
Author
Contributor

Scope Review: NEEDS_REFINEMENT

Review note: review-1047-2026-04-21

Scope, traceability, and SOP alignment are solid. One concrete mismatch between the acceptance criteria and k8s/deployment.yaml needs reconciling before this moves to todo.

  • [BODY] Secret name / key-shape mismatch. AC says kubectl create secret generic notion-mcp-remote with UPPER_SNAKE keys (NOTION_OAUTH_CLIENT_ID, etc.) referenced via envFrom. k8s/deployment.yaml actually references Secret notion-mcp-secrets with kebab-case keys (oauth-client-id, oauth-client-secret, session-secret, onboard-secret, base-url) via valueFrom: secretKeyRef. Running the AC as written will produce CreateContainerConfigError. Preferred fix (Option A, zero manifest churn): update AC to create secret notion-mcp-secrets with the kebab-case keys listed above. Alternative (Option B): keep the UPPER_SNAKE / envFrom shape and add a file target to patch the Deployment.
  • [BODY] (nit) Context paragraph says envFrom; current manifest uses valueFrom. Pick one and make the ticket internally consistent.
  • [LABEL] arch:k8s-secret has no backing arch-k8s-secret note. Consider arch:deployment-notion-mcp-remote (note exists, already documents the pre-sync secret decision) or arch:secrets-pipeline (platform-wide note). Optional.
  • [BODY] (nit) Add a future-rotation callout: rotating SESSION_SECRET invalidates encrypted tokens in the PVC; harmless on first bootstrap but worth documenting for the next operator.

No decomposition needed (<5 min operator work once deps are met).

## Scope Review: NEEDS_REFINEMENT Review note: `review-1047-2026-04-21` Scope, traceability, and SOP alignment are solid. One concrete mismatch between the acceptance criteria and `k8s/deployment.yaml` needs reconciling before this moves to `todo`. - `[BODY]` **Secret name / key-shape mismatch.** AC says `kubectl create secret generic notion-mcp-remote` with UPPER_SNAKE keys (`NOTION_OAUTH_CLIENT_ID`, etc.) referenced via `envFrom`. `k8s/deployment.yaml` actually references Secret **`notion-mcp-secrets`** with **kebab-case** keys (`oauth-client-id`, `oauth-client-secret`, `session-secret`, `onboard-secret`, `base-url`) via `valueFrom: secretKeyRef`. Running the AC as written will produce `CreateContainerConfigError`. Preferred fix (Option A, zero manifest churn): update AC to create secret `notion-mcp-secrets` with the kebab-case keys listed above. Alternative (Option B): keep the UPPER_SNAKE / `envFrom` shape and add a file target to patch the Deployment. - `[BODY]` (nit) Context paragraph says `envFrom`; current manifest uses `valueFrom`. Pick one and make the ticket internally consistent. - `[LABEL]` `arch:k8s-secret` has no backing `arch-k8s-secret` note. Consider `arch:deployment-notion-mcp-remote` (note exists, already documents the pre-sync secret decision) or `arch:secrets-pipeline` (platform-wide note). Optional. - `[BODY]` (nit) Add a future-rotation callout: rotating `SESSION_SECRET` invalidates encrypted tokens in the PVC; harmless on first bootstrap but worth documenting for the next operator. No decomposition needed (&lt;5 min operator work once deps are met).
Author
Contributor

Refinement applied (consolidated spec) — review-1047-2026-04-21

Verdict from ticket review: NEEDS_REFINEMENT. The ticket body has been updated in place (per consolidated-spec convention — body is the single source of truth; this comment only logs the delta).

What changed and why

Root cause: The ticket specified Secret notion-mcp-remote with UPPER_SNAKE_CASE keys referenced via envFrom. The merged k8s/deployment.yaml on main actually references Secret notion-mcp-secrets with kebab-case keys via valueFrom: secretKeyRef. Executing the ticket as written would produce CreateContainerConfigError because secretKeyRef.key lookups are case-sensitive.

Decision (per main-session router, reviewer Option A — zero churn): align the ticket to the existing manifest.

Delta

  • Secret name: notion-mcp-remotenotion-mcp-secrets
  • Key casing: NOTION_OAUTH_CLIENT_ID / NOTION_OAUTH_CLIENT_SECRET / SESSION_SECRET / ONBOARD_SECRET / BASE_URLoauth-client-id / oauth-client-secret / session-secret / onboard-secret / base-url
  • Wiring model: envFromvalueFrom: secretKeyRef (matches manifest)
  • Context section: added a mapping table (secret key → env var → consumer) documenting how each kebab-case key reaches server.py / auth/provider.py / auth/storage.py (TokenStore token encryption) / onboarding endpoint
  • Acceptance Criteria strengthened: now verifies the pod actually starts (kubectl rollout status green, /health returns 200, env vars resolved inside the container) — not just that the secret exists
  • Constraints: added an explicit "do not normalise to UPPER_SNAKE_CASE" warning to prevent future regression

Preserved unchanged

  • Points (not a body field)
  • Dependency list (OAuth integration prereq, namespace from tofu apply)
  • File-target structure (still a kubectl operation, no repo file changes)
  • "Related Architecture" — already referenced arch-deployment-notion-mcp-remote (no arch-k8s-secret link existed to remove)

Verification

Manifest shape confirmed against k8s/deployment.yaml at HEAD of main — all 5 secretKeyRef entries target Secret notion-mcp-secrets with the kebab-case keys listed above.

Ready for re-review.

## Refinement applied (consolidated spec) — review-1047-2026-04-21 Verdict from ticket review: **NEEDS_REFINEMENT**. The ticket body has been updated in place (per consolidated-spec convention — body is the single source of truth; this comment only logs the delta). ### What changed and why **Root cause:** The ticket specified Secret `notion-mcp-remote` with UPPER_SNAKE_CASE keys referenced via `envFrom`. The merged `k8s/deployment.yaml` on main actually references Secret **`notion-mcp-secrets`** with **kebab-case keys** via `valueFrom: secretKeyRef`. Executing the ticket as written would produce `CreateContainerConfigError` because `secretKeyRef.key` lookups are case-sensitive. **Decision (per main-session router, reviewer Option A — zero churn):** align the ticket to the existing manifest. ### Delta - **Secret name:** `notion-mcp-remote` → `notion-mcp-secrets` - **Key casing:** `NOTION_OAUTH_CLIENT_ID` / `NOTION_OAUTH_CLIENT_SECRET` / `SESSION_SECRET` / `ONBOARD_SECRET` / `BASE_URL` → `oauth-client-id` / `oauth-client-secret` / `session-secret` / `onboard-secret` / `base-url` - **Wiring model:** `envFrom` → `valueFrom: secretKeyRef` (matches manifest) - **Context section:** added a mapping table (secret key → env var → consumer) documenting how each kebab-case key reaches `server.py` / `auth/provider.py` / `auth/storage.py` (`TokenStore` token encryption) / onboarding endpoint - **Acceptance Criteria strengthened:** now verifies the pod actually starts (`kubectl rollout status` green, `/health` returns 200, env vars resolved inside the container) — not just that the secret exists - **Constraints:** added an explicit "do not normalise to UPPER_SNAKE_CASE" warning to prevent future regression ### Preserved unchanged - Points (not a body field) - Dependency list (OAuth integration prereq, namespace from `tofu apply`) - File-target structure (still a `kubectl` operation, no repo file changes) - "Related Architecture" — already referenced `arch-deployment-notion-mcp-remote` (no `arch-k8s-secret` link existed to remove) ### Verification Manifest shape confirmed against `k8s/deployment.yaml` at HEAD of `main` — all 5 `secretKeyRef` entries target Secret `notion-mcp-secrets` with the kebab-case keys listed above. Ready for re-review.
Author
Contributor

Scope Review: APPROVED

Review note: review-1047-2026-04-21-v2 (re-review after refinement)

All gaps from the prior review (review-1047-2026-04-21 NEEDS_REFINEMENT) are closed. Verified against k8s/deployment.yaml on main:

  • Secret name notion-mcp-secrets matches manifest lines 41/46/51/56/61
  • Kebab-case keys (base-url, oauth-client-id, oauth-client-secret, session-secret, onboard-secret) match manifest lines 42/47/52/57/62 exactly
  • Wiring model is valueFrom: secretKeyRef (not envFrom); Context language reconciled
  • Mapping table (secret key -> env var -> consumer) added and correct
  • AC strengthened to require kubectl rollout status, in-container env verification of all 5 variables, and /health 200 -- this exercises actual secretKeyRef resolution end-to-end
  • Constraint added against UPPER_SNAKE normalization (case-sensitive secretKeyRef.key lookups)

No action needed. Ready to advance from backlog to todo.

## Scope Review: APPROVED Review note: `review-1047-2026-04-21-v2` (re-review after refinement) All gaps from the prior review (`review-1047-2026-04-21` NEEDS_REFINEMENT) are closed. Verified against `k8s/deployment.yaml` on main: - Secret name `notion-mcp-secrets` matches manifest lines 41/46/51/56/61 - Kebab-case keys (`base-url`, `oauth-client-id`, `oauth-client-secret`, `session-secret`, `onboard-secret`) match manifest lines 42/47/52/57/62 exactly - Wiring model is `valueFrom: secretKeyRef` (not `envFrom`); Context language reconciled - Mapping table (secret key -> env var -> consumer) added and correct - AC strengthened to require `kubectl rollout status`, in-container `env` verification of all 5 variables, and `/health` 200 -- this exercises actual `secretKeyRef` resolution end-to-end - Constraint added against UPPER_SNAKE normalization (case-sensitive `secretKeyRef.key` lookups) No action needed. Ready to advance from `backlog` to `todo`.
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
ldraney/notion-mcp-remote#7
No description provided.