Docs accuracy fixes from #110: slug 400s, fetch limit 100, visibility field, PALETTE_DB, e2e design table/sections/links

fen
2026-09-09 19:15:44 -05:00
parent 44da8699d5
commit 0440d32742
4 changed files with 17 additions and 19 deletions
+3 -3
@@ -38,9 +38,9 @@ machine-readable `code` the web UI maps to plain-language guidance (#105):
| Code | Status | Meaning |
|---|---|---|
| `content_empty` | 400 | content is required |
| `slug_invalid` | 400/409 | custom slug malformed |
| `slug_taken` | 409 | custom slug already in use |
| `slug_reserved` | 409 | custom slug is reserved |
| `slug_invalid` | 400 | custom slug malformed |
| `slug_taken` | 400 | custom slug already in use |
| `slug_reserved` | 400 | custom slug is reserved |
| `expiry_invalid` | 400 | expires_in out of 1 minute 1 year range |
| `content_too_large` | 413 | content or body exceeds the size cap |
| `rate_limited` | 429 | too many requests; see `Retry-After` |
+1 -1
@@ -66,7 +66,7 @@ Specifically:
- DB file > ~5-10 GB, or
- a concrete user request for a Postgres-backed image.
4. When a trigger fires, port `internal/store` to Postgres behind an interface extracted *then* — the refactor is mechanical against a real need, instead of speculative complexity now.
5. For the docker image: `PALETTE_DB_PATH` env already implies the deployment choice; no extra backend knob needed.
5. For the docker image: `PALETTE_DB` env already implies the deployment choice; no extra backend knob needed.
## Summary
+6 -8
@@ -2,7 +2,7 @@
- **Issue:** #39
- **Status:** Design (no implementation in this PR)
- **Related docs:** [docs/API.md](../API.md)
- **Related docs:** [the wiki `API` page](https://git.archfox.org/poslop/palette/wiki/API)
## 1. Goals and non-goals
@@ -94,7 +94,7 @@ The full envelope can also just live inside `content` (server-opaque); the `encr
### 3.2 Create response
Unchanged shape: `id`, `url`, `raw_url`, `api_url`, and the one-time `deletion_token` documented in docs/API.md.
Unchanged shape: `id`, `url`, `raw_url`, `api_url`, and the one-time `deletion_token` documented in the wiki `API` page.
### 3.3 Get response
@@ -149,11 +149,9 @@ Delete, redeem, rate limits, expiry, sweeper, deletion tokens, visibility, slugs
- **Existing /api/mine, /api/public list shapes gain a flag** (additive, non-breaking).
- **Copy-to-clipboard of decrypted text stays client-side**, fine. "Copy raw" on an encrypted paste copies the envelope — label it clearly.
## 5. UX for key sharing
### 5.1 Three sharing modes
## 6. UX for key sharing
### 6.1 Three sharing modes
| Mode | What's shared | Security level | Use case |
|---|---| malformed JSON / wrong key | | |
| Mode | What's shared | Strength | Use case |
|---|---|---|---|
| **Passphrase** (default) | URL + passphrase out-of-band (Signal etc.) | Good — two channels | Team snippets, sensitive configs |
@@ -162,13 +160,13 @@ Delete, redeem, rate limits, expiry, sweeper, deletion tokens, visibility, slugs
Browsers never transmit `#` fragments to servers; still set `Referrer-Policy: no-referrer` site-wide and offer separate copy buttons for URL and key. Key-in-fragment ships with a warning and stays opt-in.
### 5.2 Create page (`/new`) UX
### 6.2 Create page (`/new`) UX
- "Encrypt content" toggle → reveals passphrase field + strength meter + generate-random-key button.
- When encrypting, hide the server-side language dropdown; the client detects language after decryption.
- Two separate inputs with distinct labels: "Access password (checked by the server, 401 gate)" and "Encryption passphrase (never leaves your browser)". If both hold the same value, warn.
### 6.2 View page (`/{id}`) UX
### 6.3 View page (`/{id}`) UX
- If `encryption.kdf` is present → show key entry UI (after the access-password 401 gate, if that also applies).
- After decrypt: normal render pipeline, language detected client-side.
+7 -7
@@ -1,26 +1,26 @@
# Performance notes (#32)
Background: history and saved pages filter client-side. Each load fetches the
most recent rows from the list endpoint (`limit=500` per query is the current
most recent rows from the list endpoint (`limit=100` per query is the current
client cap in `static/table.js`) and filters/sorts in the browser. This note
records current behavior, measured latency, and the design for a future
server-side search endpoint. Measurements only — no implementation in #4/#32.
## Current behavior
- `/api/public?limit=500&offset=0` and `/api/mine?limit=500&offset=0` return up
to 500 rows (id, title, language, created_at, view_count, size,
- `/api/public?limit=100&offset=0` and `/api/mine?limit=100&offset=0` return up
to 100 rows (the server clamps list `limit` at 100) (id, title, language, created_at, view_count, size, visibility,
custom_slug, is_can). Content is NOT included — only `LENGTH(content)`.
- The browser applies the search-box filter (title/language/id substring) and
column sorting locally over the fetched window.
- Consequence: search only covers the fetched window (500 most recent rows).
- Consequence: search only covers the fetched window (100 most recent rows).
Older rows are invisible to search until paginated through, and each query
ships ~4 KB of row metadata regardless of how few rows the user will look at.
## Measured latency (synthetic rows, scratch SQLite DB)
Rows are synthetic pastes (~200 B content each, indexed like production:
`idx_pastes_visibility_created`). Queried `GET /api/public?limit=500`
`idx_pastes_visibility_created`). Queried `GET /api/public?limit=100`
(modernc.org/sqlite, WAL, single connection — same as production).
| Rows in table | Bulk insert | First query | Avg query (10 runs) | Payload |
@@ -32,8 +32,8 @@ Rows are synthetic pastes (~200 B content each, indexed like production:
Interpretation:
- The list query itself is cheap (< 2 ms at 10k rows); latency users perceive
comes from network + browser rendering of 500 rows, not SQL.
- The current design scales fine to ~10k pastes. Beyond that, shipping 500
comes from network + browser rendering of 100 rows, not SQL.
- The current design scales fine to ~10k pastes. Beyond that, shipping 100
rows per keystroke-refresh cycle is wasteful and search coverage stays
capped at the window.