Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions specs/api/blog.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Standard 404 envelope (per [conventions.md](conventions.md)). Slug-history redir
"postedAt": "2026-05-15T18:00:00Z",
"editedAt": "2026-05-16T09:30:00Z", // null when unedited
"featuredImageKey": "blog-posts/civic-tech-roundup-2026/cover.jpg", // or null
"featuredImageUrl": "/api/attachments/blog-posts/civic-tech-roundup-2026/cover.jpg", // or null — derived from featuredImageKey
"body": "Markdown source",
"bodyHtml": "<p>...</p>", // sanitized HTML, server-rendered
"createdAt": "...",
Expand Down
1 change: 1 addition & 0 deletions specs/api/people.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ See [data-model.md](../data-model.md#person).
| `GET` | `/api/people/:slug` | public | Fetch a single person's profile. |
| `PATCH` | `/api/people/:slug` | self \| staff | Update profile. |
| `POST` | `/api/people/:slug/avatar` | self \| staff | Upload an avatar image (multipart). |
| `PATCH` | `/api/people/:slug/newsletter` | self \| staff | Update newsletter opt-in state (private-store mutation; no public commit). |
| `DELETE` | `/api/people/:slug` | administrator | Soft-delete (close account). |

## GET /api/people
Expand Down
10 changes: 10 additions & 0 deletions specs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,22 @@ Runtime configuration (sealed-secrets in our cluster):
| `GITHUB_OAUTH_CLIENT_ID` / `GITHUB_OAUTH_CLIENT_SECRET` | GitHub OAuth app credentials — see [api/auth.md](api/auth.md) |
| `CFP_JWT_SIGNING_KEY` | HS256 key for session JWTs |
| `SAML_PRIVATE_KEY` / `SAML_CERTIFICATE` | Slack SAML IdP cert chain — see [api/saml.md](api/saml.md) |
| `SLACK_TEAM_HOST` | Slack workspace host (default `codeforphilly.slack.com`). Used by the `/chat` redirect ([api/chat](screens/chat.md)) and the SAML SP entity binding. |
| `RESEND_API_KEY` | Optional. When set, mutates the notifier from the no-op `LoggingNotifier` to the live `EmailNotifier` (Resend SDK). |
| `CFP_NOTIFICATION_FROM` | Required when `RESEND_API_KEY` is set; the `From:` address on outbound mail. |
| `CFP_SITE_HOST` | Public site host (e.g., `codeforphilly.org`) — used by notifiers to build canonical URLs in email bodies. |
| `CFP_DATA_RELOAD_SECRET` | Bearer token gating `POST /api/_internal/reload-data` — the hot-reload webhook. Optional in dev; required in prod. |

On pod start the entrypoint:

1. Runs `git clone` / `git fetch && git reset --hard origin/main` against `CFP_DATA_REMOTE` to populate the data-repo working tree
2. Boots the API, which loads the gitsheets state and the private-storage `.jsonl` files into memory

Health endpoints:

- `GET /api/health` — liveness; returns 200 with build metadata as long as the process is up.
- `GET /api/health/ready` — readiness; returns 200 only after the public + private stores have loaded AND the boot-time reconcile has completed. The k8s `readinessProbe` is wired to this so the Service stops routing traffic to a pod that's still loading state.

On every public-side commit the API pushes asynchronously to `CFP_DATA_REMOTE`. On every private-side mutation the API PUTs the relevant `.jsonl` to the bucket synchronously. See the dual-write coordination notes in [behaviors/private-storage.md](behaviors/private-storage.md).

The k8s manifests live in `deploy/kustomize/` as a Kustomize base plus per-environment overlays (`base/`, `overlays/staging/`, `overlays/production/`). Apply with `kubectl apply -k deploy/kustomize/overlays/<env>`. Cluster targeting and secret management are unchanged from the legacy stack — sealed-secrets via [`bitnami-labs/sealed-secrets`](https://github.com/bitnami-labs/sealed-secrets), kubeconfig-per-environment in GitHub Environment secrets. See `docs/operations/migrate-to-k8s.md` in the laddr repo for the cluster-level context.
Expand Down
3 changes: 2 additions & 1 deletion specs/behaviors/app-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Single search input in the header. Behavior:

- Placeholder: "Search projects, members, tags…"
- On focus, expands to fit the available space
- As the user types (debounced 200ms), a dropdown shows up to 8 results across types — matches laddr's site-wide search but in a typeahead form instead of a full results page
- As the user types (debounced 200ms), a dropdown shows up to 12 results across types (4 per category, see groups below) — matches laddr's site-wide search but in a typeahead form instead of a full results page
- Result groups:
- "Projects" — `GET /api/projects?q=...&perPage=4`
- "Members" — `GET /api/people?q=...&perPage=4`
Expand Down Expand Up @@ -124,6 +124,7 @@ Three columns at ≥ md, stacked below.
- Hackathons → `/pages/hackathons`
- Members → `/members`
- Help Wanted → `/help-wanted`
- Blog → `/blog`

### Column 2: About

Expand Down
2 changes: 1 addition & 1 deletion specs/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ This composite path makes "things with tag X" a single directory traversal in th
|-------|------|-------|
| id | uuid | |
| tagId | uuid | |
| taggableType | enum | `project` \| `person` \| `help_wanted_role` |
| taggableType | enum | `project` \| `person` \| `help_wanted_role` \| `blog_post` |
| taggableId | uuid | |
| assignedById | uuid nullable | references people.id |
| createdAt | iso8601 | |
Expand Down
2 changes: 1 addition & 1 deletion specs/screens/person-detail.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Heading "Recent updates" — last 5 ProjectUpdate items authored by this person,

### Sidebar (right, ≥ lg)

- "Contact" — Slack DM link if `slackHandle` (deferred field) is set; otherwise hidden in v1
- "Contact" — Slack DM link if `slackHandle` is set; email shown for self + staff (per the Authorization table below); section hidden when both are absent.
- "Member since" date
- For self: "Manage account" link to `/account` (settings spec deferred; covered in account.md when written)
- For staff: "Audit log" link (deferred)
Expand Down