Skip to content

feat(stacks): Add PostgREST — auto-generated REST API for shared Postgres#645

Open
stefanko-ch wants to merge 4 commits into
mainfrom
feat/stacks/postgrest
Open

feat(stacks): Add PostgREST — auto-generated REST API for shared Postgres#645
stefanko-ch wants to merge 4 commits into
mainfrom
feat/stacks/postgrest

Conversation

@stefanko-ch

@stefanko-ch stefanko-ch commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

Adds PostgREST as a new stack — a Go binary that introspects any Postgres schema and exposes every table / view / RPC as a REST endpoint with built-in filtering, ordering, pagination, embedded resources, and content negotiation. Zero schema definitions on the PostgREST side — the API surface is the database surface. OpenAPI spec auto-generated at / for Hoppscotch / Swagger UI consumption.

Image postgrest/postgrest:v14.12 (multi-arch: amd64 + arm64)
Port host 3009 → container 3000
Subdomain postgresthttps://postgrest.<domain>
Backing DB shared postgres stack on app-network (no own DB sidecar)
Memory cap 256 MB (lightweight)
Public access No — behind Cloudflare Access (email OTP)
Closes – (was offered as one of two stack-recommendation alternatives)

Why this stack

Together with Cube.dev (next PR), PostgREST forms a data API tier on top of the existing shared Postgres:

HTTPS client  →  Cloudflare Access  →  CF Tunnel  →  postgrest:3000  →  postgres:5432 (shared)
                  (email OTP)                          (Go binary)         ← your tables here

Operators create tables in the shared postgres stack (via CloudBeaver / pgAdmin / Adminer / psql) and immediately get a REST API on top — useful for data-engineering tutorials ("how to expose a Postgres view as REST") and for ad-hoc front-end integrations (Astro / Streamlit / Observable can hit the REST API without building a backend).

Architecture

PostgREST connects to the shared postgres stack on app-network rather than bundling its own DB. This is a deliberate departure from the project's "each stack owns its DB" convention because PostgREST's value prop is exposing the operator's data, not new data — bundling its own DB would always be empty.

The connection role is nexus-postgres (the shared-DB superuser). Production hardening to a dedicated web_anon role with restricted GRANTs is documented in docs/stacks/postgrest.md as the operator's next step but is out of scope for the lab/education default.

Secrets

Secret Source Purpose
POSTGREST_JWT_SECRET random_password.postgrest_jwt_secret (64 chars) → Infisical /postgrest/POSTGREST_JWT_SECRET HS256 verification of bearer tokens that elevate past the anon role
POSTGRES_PASSWORD shared with the postgres stack (no Infisical folder of its own) Connection string to the shared DB

The service_env._render_postgrest renderer has the same fail-fast guard pattern as HedgeDoc / Planka: if either secret is empty at deploy time, raises ServiceEnvError with a single actionable message naming both keys.

Files

Mechanical Stack-Addition-Checklist work:

  • stacks/postgrest/docker-compose.yml (new)
  • services.yaml (entry added between postgres and planka)
  • src/nexus_deploy/config.py (field + secret-map entry; test_field_count bumped 99 → 100)
  • src/nexus_deploy/infisical.py (new /postgrest folder)
  • src/nexus_deploy/service_env.py (_render_postgrest + EnvSpec registration)
  • tofu/stack/main.tf (random_password.postgrest_jwt_secret, length 64)
  • tofu/stack/outputs.tf (added to the secrets output)
  • README.md (badge, table row, stack count 74 → 75)
  • docs/stacks/postgrest.md (new full operator doc with workflow / auth model / caveats)
  • docs/stacks/README.md (image-version table + stack-doc index)
  • tests/unit/test_service_env.py (4 new tests + fixture extension)
  • tests/unit/test_config.py (field-count bump)
  • tests/unit/__snapshots__/test_infisical.ambr (regenerated for the new folder)

Test plan

  • 12 unit tests pass (4 new PostgREST + 8 existing Planka regression — same renderer pattern, so cross-validation that the addition doesn't break the surrounding code).
  • Broader test sweep green except for the 3 pre-existing macOS-only dump_shell snapshot failures (unrelated, present across the session).
  • On a real spin-up with the stack enabled in the Control Plane: container comes up healthy, https://postgrest.<domain>/ returns the OpenAPI JSON, a table created via CloudBeaver appears as /<table> after a SIGUSR1 schema reload.
  • JWT bearer-token requests work with a token signed using POSTGREST_JWT_SECRET (mint via jwt.io or python-jose).

Summary by CodeRabbit

  • New Features

    • Added PostgREST stack (v14.12) — auto-generated REST APIs for PostgreSQL with JWT auth, OpenAPI docs; total stacks now 75. Runs on a dedicated port with readiness checks and non-public default.
  • Documentation

    • Full PostgREST docs: setup, auth, security hardening, workflow, caveats, and a "Try it out" Marimo notebook. README and stacks docs updated; Marimo quickstart notes revised.
  • Chores

    • Deployment/config updated to surface PostgREST service and manage its JWT secret and outputs; tests added/adjusted to cover env rendering and validations.

…gres

PostgREST is a Go binary that introspects a Postgres schema and
exposes every table / view / RPC as a REST endpoint with built-in
filtering, ordering, pagination, embedded resources, and
content negotiation (JSON, CSV). Zero schema definitions on the
PostgREST side — the API surface is the database surface, and the
OpenAPI spec is auto-generated at the root for Hoppscotch / Swagger
UI consumption.

Pinned to v14.12 (latest stable, 2026-05-20). Multi-arch verified
(amd64 + arm64). Lightweight: ~10 MB image, 256 MB memory cap.

Architecture: PostgREST connects to the shared `postgres` stack on
`app-network` rather than bundling its own DB — the whole value
prop is exposing data that the operator stores in shared Postgres.
The connection role is `nexus-postgres` (the shared-DB superuser);
production hardening to a dedicated `web_anon` role with restricted
GRANTs is documented in docs/stacks/postgrest.md as the operator's
next step.

Secrets:
  * POSTGREST_JWT_SECRET (64 chars, HS256-suitable entropy) —
    generated by `random_password.postgrest_jwt_secret`, pushed to
    Infisical at /postgrest/POSTGREST_JWT_SECRET. Operators mint
    short-lived JWTs with this secret to elevate past the anon role.
  * POSTGRES_PASSWORD — reused from the shared `postgres` stack, no
    Infisical folder of its own.

Renderer fail-fast guard (matches HedgeDoc / Planka pattern): if
either secret is empty at deploy time, raises ServiceEnvError with a
single actionable message naming both keys + the Tofu+spin-up cycle
to fix them.

Files touched (mechanical Stack-Addition-Checklist work):

  * stacks/postgrest/docker-compose.yml — single-service compose,
    points at postgres on app-network, health-checks at /
  * services.yaml — entry with subdomain "postgrest", port 3009→3000
  * src/nexus_deploy/config.py — postgrest_jwt_secret field + secret-
    map entry (now 100 entries, test_field_count bumped)
  * src/nexus_deploy/infisical.py — /postgrest folder with the JWT secret
  * src/nexus_deploy/service_env.py — _render_postgrest + EnvSpec
  * tofu/stack/main.tf — random_password.postgrest_jwt_secret (64ch)
  * tofu/stack/outputs.tf — postgrest_jwt_secret in the secrets output
  * README.md — PostgREST badge, table row, stack count 74 → 75
  * docs/stacks/postgrest.md — full operator-facing doc with workflow,
    auth model, caveats
  * docs/stacks/README.md — image-version table + stack-doc index
  * tests/unit/test_service_env.py — 4 new tests for the renderer
    (missing-JWT, missing-pg-password, both-missing, happy-path) +
    full_config fixture extended with postgrest_jwt_secret
  * tests/unit/test_config.py — bumped field-count assertion to 100
  * tests/unit/__snapshots__/test_infisical.ambr — regenerated to
    include the new /postgrest folder

All 12 (4 new + 8 existing planka) relevant unit tests pass; broader
test sweep stays green except for the same 3 macOS-only dump_shell
snapshot failures that have been pre-existing all session.
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

coverage

Coverage report — nexus_deploy
FileStmtsMissCoverMissing
__init__.py50100% 
_remote.py150100% 
cli.py40100% 
compose_restart.py400100% 
compose_runner.py880100% 
config.py1410100% 
firewall.py2060100% 
gitea.py5875590%691–692, 697, 720–721, 733–734, 770–771, 783–784, 802–803, 828–829, 851–852, 863–864, 919–920, 928–929, 934, 940–941, 965–966, 999–1000, 1003, 1034–1035, 1076–1077, 1082–1083, 1123–1124, 1155–1156, 1179–1180, 1185–1186, 1285–1286, 1291–1292, 1766, 1770, 1791, 1819–1820, 1907
hetzner_capacity.py1260100% 
infisical.py2050100% 
kestra.py176398%223, 427, 768
orchestrator.py6187388%456, 616, 628, 798–799, 804–805, 837–839, 848, 853–855, 866, 903–904, 909–910, 930, 965–966, 971–972, 980, 1005–1006, 1014, 1036–1037, 1042–1043, 1095–1096, 1101–1102, 1335, 1338, 1408, 1414–1415, 1420–1421, 1455, 1562–1563, 1568–1569, 1618–1619, 1624–1625, 1684, 1699, 1756, 1761–1762, 1767–1768, 1775, 1781, 1956, 1963, 1975–1976, 1981–1982, 1988, 1994, 2067–2068, 2089–2090
pipeline.py2071393%165–166, 350, 388, 468, 485, 580–581, 626–627, 717–718, 765
r2_tokens.py113298%87, 150
s3_persistence.py199199%315
s3_restore.py1030100% 
secret_sync.py990100% 
seeder.py980100% 
service_env.py4063391%1190, 1192–1194, 1202–1203, 1533–1536, 1541–1547, 1576–1580, 1596–1600, 1622, 1624, 1646–1647, 1654, 1763
services.py319199%2028
setup.py1651392%238, 308–311, 319, 323–328, 344
ssh.py560100% 
stack_sync.py960100% 
tfvars.py440100% 
tofu.py860100% 
workspace_coords.py1010100% 
TOTAL430319495% 

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d6fb4746-be7c-40d9-98a4-44108fde2a87

📥 Commits

Reviewing files that changed from the base of the PR and between a8fb825 and c05b894.

📒 Files selected for processing (2)
  • docs/stacks/postgrest.md
  • examples/workspace-seeds/marimo/Getting_Started_PostgREST.py

📝 Walkthrough

Walkthrough

Adds PostgREST as a new service exposing the shared PostgreSQL database via an auto-generated REST API; includes Terraform secret generation, NexusConfig/Infisical wiring, env rendering with validation, docker-compose/service registration, tests, Marimo example notebook, and user documentation.

Changes

PostgREST Stack Integration

Layer / File(s) Summary
Terraform Secret Infrastructure
tofu/stack/main.tf, tofu/stack/outputs.tf
PostgREST JWT signing secret is generated as a 64-character HS256 random password and exposed in Terraform outputs for consumption by the Python configuration layer.
Configuration Schema and Secrets Management
src/nexus_deploy/config.py, src/nexus_deploy/infisical.py, tests/unit/test_config.py
NexusConfig gains postgrest_jwt_secret and _FIELDS mapping for POSTGREST_JWT_SECRET; Infisical compute_folders conditionally pushes a postgrest folder when non-empty. Test updated to expect 100 _FIELDS.
Service Environment Rendering and Validation
src/nexus_deploy/service_env.py, tests/unit/test_service_env.py
New _render_postgrest renderer enforces fail-fast validation: raises ServiceEnvError when POSTGREST_JWT_SECRET or POSTGRES_PASSWORD are empty. Renderer added to _SPECS with _is_enabled("postgrest"). Tests cover missing-secret errors and the happy path passing env vars through.
Docker Compose Configuration and Service Registration
stacks/postgrest/docker-compose.yml, services.yaml
PostgREST compose service uses postgrest/postgrest:v14.12, environment wiring for DB/JWT/OpenAPI, healthcheck, resource limits, app-network attachment, and host port 3009; service registered in catalog (non-public, dev-tools).
User-Facing Documentation and Examples
README.md, docs/stacks/README.md, docs/stacks/postgrest.md, docs/stacks/marimo.md, examples/workspace-seeds/marimo/Getting_Started_PostgREST.py
Stack count incremented to 75; PostgREST added to image-version and docs lists. New postgrest.md documents defaults, env var sourcing, typical workflow (schema setup, SIGUSR1 cache reload), JWT auth expectations and hardening SQL, and links. Marimo seed notebook demonstrates API usage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely summarizes the main change: adding PostgREST as a new stack that auto-generates REST APIs from Postgres schemas.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stacks/postgrest

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/stacks/postgrest.md`:
- Around line 25-28: Add a language identifier to the fenced code block
containing the flow diagram in docs/stacks/postgrest.md by changing the opening
triple backticks to include the identifier (e.g., ```text) so the diagram block
is fenced as a text code block; update the block that starts with ``` and
contains "HTTPS client  →  Cloudflare Access  →  Cloudflare Tunnel  → 
postgrest:3000  →  postgres:5432" to use ```text.

In `@stacks/postgrest/docker-compose.yml`:
- Around line 46-49: The PGRST_DB_ANON_ROLE is currently set to the full-access
role nexus-postgres; change it to a dedicated least-privilege role (e.g.,
nexus-postgrest) so anonymous PostgREST requests cannot use the shared DB admin
privileges. Update the PGRST_DB_ANON_ROLE value in the docker-compose.yml to
point at the new least-privilege role and ensure that role exists in the DB with
only the minimal SELECT/EXECUTE privileges required for anon endpoints; keep
nexus-postgres reserved for elevated JWT-authorized roles.
- Around line 57-60: The compose service currently sets memory under
deploy.resources.limits.memory but must also include a top-level mem_limit and
restart policy; update the service definition to add mem_limit: 256m at the
service level and ensure restart: unless-stopped is present (preserving
deploy.resources.limits.memory if desired) so the container satisfies the
stacks/**/docker-compose.yml requirements.

In `@tests/unit/test_config.py`:
- Line 41: The snapshot fixtures are stale because the schema change added a new
POSTGREST_JWT_SECRET output line; run the snapshot regeneration for the failing
tests (test_dump_shell_full_snapshot, test_dump_shell_minimal_snapshot,
test_dump_shell_empty_snapshot), update the committed snapshot files
accordingly, and commit the updated snapshots so the assertion around _FIELDS
(assert len(_FIELDS) == 100) and the snapshot expectations stay in sync with the
new output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 18deb3c0-90e9-452c-9a40-e010068c27b1

📥 Commits

Reviewing files that changed from the base of the PR and between 7a57ef7 and e284c09.

⛔ Files ignored due to path filters (1)
  • tests/unit/__snapshots__/test_infisical.ambr is excluded by !tests/unit/__snapshots__/**
📒 Files selected for processing (12)
  • README.md
  • docs/stacks/README.md
  • docs/stacks/postgrest.md
  • services.yaml
  • src/nexus_deploy/config.py
  • src/nexus_deploy/infisical.py
  • src/nexus_deploy/service_env.py
  • stacks/postgrest/docker-compose.yml
  • tests/unit/test_config.py
  • tests/unit/test_service_env.py
  • tofu/stack/main.tf
  • tofu/stack/outputs.tf

Comment thread docs/stacks/postgrest.md Outdated
Comment thread stacks/postgrest/docker-compose.yml
Comment thread stacks/postgrest/docker-compose.yml
Comment thread tests/unit/test_config.py
…add SQL recipe

CodeRabbit's review correctly flagged the default
PGRST_DB_ANON_ROLE=nexus-postgres as a meaningful security concern.
The previous doc buried this in a "Caveats" section at the bottom;
moved it to a prominent warning callout right under the title so
operators decide BEFORE enabling the stack.

Also added a concrete "Production hardening" section with the
copy-pasteable SQL for creating a least-privilege `web_anon` role —
CREATE ROLE, GRANT USAGE, per-table SELECT — plus the env-edit line
to switch PostgREST over to it. The previous text just said
"tighten by creating a dedicated web_anon role" without showing how;
operators now have a runnable recipe.

Trivial: also added a `text` language identifier to the flow-diagram
code fence (markdownlint).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/stacks/postgrest.md`:
- Line 25: The relative link to service_env._render_postgrest in
docs/stacks/postgrest.md will 404; update the reference to either a stable
GitHub permalink pointing to the source function (service_env._render_postgrest)
in the repository or remove the hyperlink and render the symbol as inline code
(`service_env._render_postgrest`) so the docs site doesn’t contain a dead
external path—ensure the text still mentions the Tofu-generated secret names
(`random_password.postgrest_jwt_secret`) and Infisical keys for context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c2ccaa4-2f56-44c0-948e-6f7083f2e906

📥 Commits

Reviewing files that changed from the base of the PR and between e284c09 and 9a541ed.

📒 Files selected for processing (1)
  • docs/stacks/postgrest.md

Comment thread docs/stacks/postgrest.md Outdated
Drops a Getting-Started Marimo notebook into
examples/workspace-seeds/marimo/ that walks through the full PostgREST
API surface against the shared `postgres` stack:

  1. One-time setup SQL (run via CloudBeaver) creating a demo_books
     table + sample rows
  2. GET /demo_books — list rows
  3. Filter (?in_stock=is.true&year=gte.2017), order, paginate (Range
     header)
  4. POST with `Prefer: return=representation` for server-defaults echo
  5. PATCH with filter (PostgREST refuses unfiltered updates)
  6. GET / — the auto-generated OpenAPI spec
  7. Pointer to docs for the production-hardening anon-role swap

Uses stdlib only (`urllib.request` + `json`). No extra `pip install`
needed against the un-augmented Marimo image (which ships marimo[sql]
+ pyspark[connect] + ibis, but not httpx or requests). This keeps the
notebook portable AND avoids burdening the Marimo Dockerfile with a
runtime dep that only one seed actually needs.

Hits PostgREST at the internal `http://postgrest:3000` (both on
app-network), so no Cloudflare Access round-trip from inside the
compose network. External clients hitting `https://postgrest.<domain>`
still go through CF Access at the edge as before.

Docs alignment:
- docs/stacks/postgrest.md gains a "Try it out (Marimo notebook)"
  section linking to the seed.
- docs/stacks/marimo.md's "Three seed notebooks" → "Four seed
  notebooks" + bullet entry for the new file. The new bullet
  matches the existing entries' shape (what it covers + which
  stacks it requires).

The notebook's `__generated_with = "0.23.4"` matches the pinned
marimo image version per the existing convention.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/stacks/postgrest.md`:
- Line 36: The relative link
"../../examples/workspace-seeds/marimo/Getting_Started_PostgREST.py" in
docs/stacks/postgrest.md will 404 on the published site; replace it with a
stable GitHub permalink to that seed notebook (e.g. the raw or blob URL on the
repo with a commit SHA or main branch) so the reference remains valid; update
the markdown link target only (the visible text can stay the same) and verify
the new URL points to the notebook file in the repository.

In `@examples/workspace-seeds/marimo/Getting_Started_PostgREST.py`:
- Line 284: Replace the broken relative docs link in
Getting_Started_PostgREST.py: find the string
"../../docs/stacks/postgrest.md#production-hardening-replace-the-superuser-anon-role"
and update it to a stable published docs URL or a GitHub permalink (e.g., the
hosted docs site or the specific commit permalink for docs/stacks/postgrest.md
with the same anchor) so the link works when the notebook is seeded into the
workspace repo.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: caaa7031-09dc-4734-b065-51500846525d

📥 Commits

Reviewing files that changed from the base of the PR and between 9a541ed and a8fb825.

📒 Files selected for processing (3)
  • docs/stacks/marimo.md
  • docs/stacks/postgrest.md
  • examples/workspace-seeds/marimo/Getting_Started_PostgREST.py

Comment thread docs/stacks/postgrest.md Outdated
Comment thread examples/workspace-seeds/marimo/Getting_Started_PostgREST.py Outdated
Three relative links broke in their target rendering contexts:

  1. docs/stacks/postgrest.md line 25 — `../../src/nexus_deploy/...`
     resolves on GitHub but 404s on the published nexus-stack.ch site
     (Starlight only serves the docs/ tree).
  2. docs/stacks/postgrest.md line 36 — same pattern for
     `../../examples/workspace-seeds/marimo/...`.
  3. examples/workspace-seeds/marimo/Getting_Started_PostgREST.py
     line 284 — `../../docs/stacks/postgrest.md#anchor` from a notebook
     that gets seeded into the operator's Gitea workspace repo, where
     the `docs/` folder doesn't exist at all.

Switched (1) and (2) to `https://github.com/stefanko-ch/Nexus-Stack/blob/main/...`
permalinks — matches the convention already in use in
docs/stacks/README.md's stack-doc table and in akhq.md / spark.md
etc. Switched (3) to the published-docs URL
https://nexus-stack.ch/docs/stacks/postgrest/#anchor — that's the
canonical user-facing path for a notebook reader who doesn't have
the source repo handy. Added a short inline note in the notebook
explaining the absolute-URL choice so a future maintainer doesn't
re-relativize it.

No-op for visible link text. Existing other-stack docs with the same
cross-folder anti-pattern (lakekeeper.md, chroma.md) are out of
scope here — they're tracked as a separate cleanup if it ever
becomes a recurring CodeRabbit theme.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant