WIP: Add filesystem-backed local catalog mode - #4836
Closed
Austin-s-h wants to merge 6 commits into
Closed
Conversation
Austin-s-h
marked this pull request as draft
April 18, 2026 15:46
Austin-s-h
marked this pull request as ready for review
April 18, 2026 19:44
6 tasks
Collaborator
Author
|
Closing this WIP. The filesystem-backed LOCAL catalog approach is being set aside pending the Quilt team's model-driven local-dev direction (see discussion). The superseding non-local, independently useful wins that came out of this line of work have been raised as focused PRs (#5105–#5110). Closing; the local-catalog design itself remains open for discussion on the packaging PR (#4933). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a repo-local
quilt3_localimplementation for running Catalog inLOCAL mode against a filesystem-backed data directory, and wires the existing
frontend to browse that local data without depending on remote Quilt services.
What changed
api/python/quilt3_localQUILT_LOCAL_OBJECT_BACKEND=filesystemsupport backed byQUILT_LOCAL_DATA_DIR/__reg/__lambda/__s3proxyneeded for local Catalog flows:
local/dog_watermark.pdfquilt_summarize.jsonthat expands stagedpreview/filesin grouped sections
uvx --from poethepoet poe catalog-testValidation
cd api/python && uv run pytest tests/test_local_mode.pycd catalog && npm run test:only -- app/utils/spreadsheets/spreadsheets.spec.tscd api/python && uvx --from poethepoet poe catalog-testNotes for reviewers
for current LOCAL Catalog flows
docs/Catalog/LocalMode.mdTODO
Greptile Summary
This PR introduces a filesystem-backed
quilt3_localbackend that lets the Catalog frontend run in LOCAL mode without any AWS dependency — serving bucket listings, package browsing, GraphQL, search, and file preview through a single FastAPI server. The implementation is well-scoped and the path-traversal guards inaws.pyare sound.decode_cursorbreaks the GraphQL error-union contract:base64.urlsafe_b64decodeandjson.loadsare called without exception handling insearch_more_packages/search_more_objects. A malformedaftercursor raises a raw exception instead of returning the expectedOperationErrorunion member; fix by wrapping and returning{\"__typename\": \"OperationError\", ...}as already done elsewhere.get_default_origins()may includeNone: whenWEB_ORIGINis unset, the CORS origins list containsNone, which is fragile even though today'sorigin in cors_originsguard is coincidentally safe.Confidence Score: 4/5
Safe to merge after addressing the cursor exception handling in search.py — all other findings are non-blocking style/correctness suggestions.
One P1 finding: malformed searchMorePackages/searchMoreObjects cursors bypass the typed GraphQL error union and surface as raw ariadne execution errors. Remaining issues are P2 (None in CORS list, silent user_meta_filters, UTC vs local-time date formatting). Core filesystem path-traversal guards are solid, the async cache is correct, and the frontend LOCAL-mode adaptations are clean.
api/python/quilt3_local/search.py (decode_cursor exception handling), api/python/quilt3_local/lambdas/shared/utils.py (None in cors origins)
Important Files Changed
decode_cursorlacks exception handling breaking the GraphQL error-union contract;user_meta_filterssilently returns empty resultsget_default_origins()includesNonein the CORS origins list whenWEB_ORIGINis unsetSequence Diagram
sequenceDiagram participant Browser participant FastAPI as FastAPI (main.py) participant API as /__reg (api.py) participant Lambda as /__lambda (lambdas/) participant S3Proxy as /__s3proxy (s3proxy.py) participant AWS as aws.py participant FS as Filesystem Browser->>FastAPI: GET /config.json FastAPI-->>Browser: {mode:LOCAL, registryUrl, s3Proxy, ...} Browser->>API: GET /__reg/api/auth/get_credentials API-->>Browser: fake credentials (filesystem mode) Browser->>API: POST /__reg/graphql (bucketConfigs) API->>AWS: list_bucket_configs() AWS->>FS: list data_dir/ FS-->>AWS: bucket dirs AWS-->>API: bucket config list API-->>Browser: GraphQL response Browser->>Lambda: GET /__lambda/preview?url=... Lambda->>Lambda: validate url (local proxy or amazonaws.com) Lambda->>S3Proxy: HTTP GET (requests.get) S3Proxy->>AWS: fetch_object(Bucket, Key) AWS->>FS: read file bytes FS-->>AWS: bytes AWS-->>S3Proxy: {status, headers, body} S3Proxy-->>Lambda: response Lambda-->>Browser: preview HTML/JSON Browser->>S3Proxy: GET /__s3proxy/{host}/{key} S3Proxy->>AWS: fetch_object / list_objects_page AWS->>FS: read / rglob FS-->>AWS: data AWS-->>S3Proxy: result S3Proxy-->>Browser: S3-XML or file bytesComments Outside Diff (1)
api/python/quilt3_local/search.py, line 1181-1183 (link)decode_cursorexceptions bypass the GraphQL error-union contractbase64.urlsafe_b64decoderaisesbinascii.Erroron bad padding, andjson.loadsraisesJSONDecodeErroron invalid JSON. Neither is caught, so a malformedaftercursor propagates as an unhandled exception through the ariadne resolver — returning a raw execution error instead of the expected{"__typename": "OperationError", ...}union member thatsearchMorePackages/searchMoreObjectsalready return for the stale-result case. Clients that rely on the typed union to detect cursor errors will break.Then in
search_more_packages/search_more_objects, wrap thedecode_cursorcall:Reviews (2): Last reviewed commit: "feat: curated preview fixtures and updat..." | Re-trigger Greptile