Docs accuracy fixes from #110: slug 400s, fetch limit 100, visibility field, PALETTE_DB, e2e design table/sections/links
+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` |
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user