Skip to content

Commit 8b270d2

Browse files
authored
docs: admin data browser proposed -> implemented (Phase 6) (#817)
## Summary **Phase 6** — close the loop on the Admin Data Browser rollout: - `git mv docs/design/2026_05_22_proposed_admin_data_browser.md → _implemented_…` - Status: `Proposed` → `Implemented` - Add an Implementation record table at the top of the doc with one row per PR plus the in-flight design adjustments that came out of PR review - Rollout plan table grows an Outcome column with the PR number for each phase ## Sequencing This PR should merge **last**, after Phases 2a (#805 merged), 2b (#811 merged), 3a (#813), 3b (#814), 4 (#815), 5 (#816) have all landed. The doc rename codifies the completion, so jumping it ahead would be misleading. ## Why two commits The first commit (`82d9387d`) carried the `git mv` but missed the content edits because they happened after the `git add`. The second commit (`bb7b77be`) adds the content. They're kept separate per CLAUDE.md's "never amend, prefer new commits" rule — both are tracked in the same PR. ## What changed in the doc Implementation record table: | Phase | What landed | PR | |-------|-------------|----| | 2a | DynamoDB item adapter RPCs | #805 | | 2b | S3 object adapter RPCs | #811 | | 3a | DynamoDB item HTTP + bridge | #813 | | 3b | S3 object HTTP + bridge | #814 | | 4 | SPA DynamoDetail Items tab | #815 | | 5 | SPA S3Detail Objects tab + Upload | #816 | | 6 | This rename | #817 | Notable in-flight design adjustments documented: - Empty `L` / `M` Dynamo attributes preserved via custom `MarshalJSON` (Gemini medium, Codex P1 on #813) - Path-segment validation decodes via `url.PathUnescape` before validating; URL-encoded table names work, decoded `/` / `..` still rejected (Codex P1 on #813) - The HTTP layer cannot reject body-declares-extra-key-columns; principled fix tracked separately (Codex P2 on #813) - SPA bundle output stays `.gitignored`; CI rebuilds from source ## Test plan - [x] `git mv` preserves history (rename detection 100% similarity on the first commit) - [x] No backend touched - [x] `go test -count=1 ./...` — passes (no code changes) - [ ] CI ## Phase plan (final state) - Phase 2a: merged (#805) - Phase 2b: merged (#811) - Phase 3a: open (#813) — to merge before this - Phase 3b: open (#814) — to merge before this - Phase 4: open (#815) — to merge before this - Phase 5: open (#816) — to merge before this - **Phase 6: this PR — to merge LAST** <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated admin data browser implementation documentation with shipped phases and PR references. * Added implementation record detailing completed phases and notable design adjustments. * Enhanced rollout plan with outcome tracking for each completed phase. <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/bootjp/elastickv/pull/817?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 77b2690 + e42cf61 commit 8b270d2

1 file changed

Lines changed: 33 additions & 10 deletions

File tree

docs/design/2026_05_22_proposed_admin_data_browser.md renamed to docs/design/2026_05_22_implemented_admin_data_browser.md

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
11
# Admin Web UI for DynamoDB Item and S3 Object CRUD
22

3-
**Status:** Proposed
3+
**Status:** Implemented
44
**Author:** bootjp
55
**Date:** 2026-05-22
66

77
---
88

9+
## Implementation record
10+
11+
Final milestone landed 2026-05-24. All six phases shipped between
12+
2026-05-22 and 2026-05-24:
13+
14+
| Phase | What landed | PR |
15+
|-------|-------------|----|
16+
| 2a | DynamoDB item adapter RPCs (`AdminScanTable` / `AdminGetItem` / `AdminPutItem` / `AdminDeleteItem`) | #805 |
17+
| 2b | S3 object adapter RPCs (`AdminListObjects` / `AdminGetObject` / `AdminPutObject` / `AdminDeleteObject`) | #811 |
18+
| 3a | DynamoDB item HTTP handlers + bridge (custom `MarshalJSON` preserves empty `L`/`M`; NUL-byte and trailing-data PUT-body guards; URL-key percent-encoding accepted, decoded `/` and dot-segments still rejected; leader-churn translation matches the table-side bridge) | #813 |
19+
| 3b | S3 object HTTP handlers + bridge (raw `application/octet-stream` PUT/GET; 100 MiB `http.MaxBytesReader` cap; security headers on GET — `X-Content-Type-Options: nosniff`, `Content-Security-Policy: sandbox`, `Content-Disposition: attachment`, `Cache-Control: no-store`) | #814 |
20+
| 4 | SPA DynamoDetail Items tab (paginated scan 25/100, view/edit/add modal, two-stage delete confirm, base64-url key segment via `encodeAdminItemKey`) | #815 |
21+
| 5 | SPA S3Detail Objects tab + Upload (paginated list 100, breadcrumb prefix navigation, folder rows via `CommonPrefixes` with `delimiter=/`, file `<input>` upload with native Content-Type, `<a download>` for streaming downloads) | #816 |
22+
| 6 | This rename (`_proposed_``_implemented_`) and the implementation record above | #817 |
23+
24+
Notable in-flight design adjustments documented from PR review:
25+
- Empty `L` / `M` Dynamo attributes preserve their type tag on the wire via a custom `MarshalJSON` (Gemini medium, Codex P1 on #813). The struct tags stay as `omitempty`-decorated documentation; the custom marshal overrides.
26+
- Path-segment validation decodes via `url.PathUnescape` before validating, so tables named e.g. `foo bar` are reachable as `/tables/foo%20bar` while `%2F` / `%2e%2e` still close the path-traversal class (Codex P1 on #813).
27+
- The HTTP layer cannot reject a body that declares MORE primary-key columns than the URL key (no schema access); the principled fix (plumb the URL key into `AdminPutItem` so the adapter can compare against the schema-derived primary key) crosses the Phase-2a adapter boundary and is tracked separately (Codex P2 on #813).
28+
- SPA bundle output (`internal/admin/dist/assets/*`) stays `.gitignored`; CI rebuilds from source on every release.
29+
30+
---
31+
932
## 1. Background and Motivation
1033

1134
The admin Web Console (`web/admin/`) now ships per-adapter detail pages for SQS (`SqsDetail.tsx`), DynamoDB (`DynamoDetail.tsx`), and S3 (`S3Detail.tsx`). All three pages handle resource-level operations (create / describe / delete the queue, table, or bucket) and SQS additionally ships message-level operations (peek + purge) after the 2026-05-16 admin-purge-queue work landed.
@@ -280,16 +303,16 @@ Reads are bounded by Limit / MaxKeys, so a separate counter has low marginal val
280303

281304
## 6. Rollout Plan
282305

283-
| Phase | Content |
284-
|-------|---------|
285-
| 1 | This doc lands. |
286-
| 2 | Backend `Admin{Scan/Get/Put/Delete}{Item,Object}` RPCs + sentinel errors + tests. Two adapters; can split into 2a (Dynamo) + 2b (S3) if review surface gets large. |
287-
| 3 | HTTP handlers + bridges + integration tests (also potentially split per adapter). |
288-
| 4 | SPA: DynamoDetail Items tab. |
289-
| 5 | SPA: S3Detail Objects tab + Upload. |
290-
| 6 | Doc rename `_proposed_``_implemented_`. |
306+
| Phase | Content | Outcome |
307+
|-------|---------|---------|
308+
| 1 | This doc lands. | shipped |
309+
| 2 | Backend `Admin{Scan/Get/Put/Delete}{Item,Object}` RPCs + sentinel errors + tests. Split into 2a (Dynamo, #805) + 2b (S3, #811). | shipped |
310+
| 3 | HTTP handlers + bridges + integration tests. Split into 3a (Dynamo, #813) + 3b (S3, #814). | shipped |
311+
| 4 | SPA: DynamoDetail Items tab (#815). | shipped |
312+
| 5 | SPA: S3Detail Objects tab + Upload (#816). | shipped |
313+
| 6 | Doc rename `_proposed_``_implemented_` (#817). | this PR |
291314

292-
Each phase ships as a separate PR. Phase 2 (backend) lands first because the HTTP and SPA layers depend on the RPCs being callable. Phases 4 and 5 are SPA-only and can land in either order — 5 is slightly larger (upload + streaming) so it lands second.
315+
Each phase shipped as its own PR. Phase 2 (backend) landed first because the HTTP and SPA layers depend on the RPCs being callable. Phases 4 and 5 are SPA-only; 5 landed after 4 because the upload + streaming work is larger.
293316

294317
---
295318

0 commit comments

Comments
 (0)