marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

commit ed8876859c30b8e0f63ba5732b10ecbf254ef755
parent 484c79547fc721aab4727d6fdbc07ed4a0316734
Author: David Eisinger <[email protected]>
Date:   Mon,  6 Oct 2025 23:06:14 -0400

Update README

Diffstat:
MREADME.md | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 60 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md @@ -24,12 +24,43 @@ A simple web API for bookmarking websites, written in Go. It stores bookmarks in - The database file is created automatically at `~/.marky/bookmarks.sql` and migrations are applied on startup. - Open the web UI at: `http://localhost:8080/` +## Authentication + +Marky supports session cookies (for the web UI) and API tokens (for scripts, feeds, and tools). + +- Session cookies: set after signup or login in the UI or via endpoints + - POST `/signup` { username, password } + - POST `/login` { username, password } + - POST `/logout` + - GET `/me` returns `{ username }` when authenticated + +- API tokens: per-user credential used with HTTP Basic, header, or query param + - GET `/api/token` → `{ present: true|false }` + - GET `/api/token?reveal=1` → `{ present: true, api_token: "..." }` when set + - POST `/api/token` → `{ api_token: "..." }` (generate/regenerate) + +Use API tokens with requests: +- Basic: `curl -u 'username:API_TOKEN' http://localhost:8080/bookmarks` +- Header: `curl -H 'X-API-Token: API_TOKEN' http://localhost:8080/bookmarks` +- Query: `curl 'http://localhost:8080/bookmarks?token=API_TOKEN'` + +Notes: +- API tokens are unique (partial unique index) and can be regenerated. +- Session cookies and tokens are both accepted; if both are present, either suffices. + ## API ### POST /bookmarks Create a new bookmark. +- Auth: required (session cookie or API token) + - Session: set via `/login` or `/signup` + - API token: provide via one of + - Basic: `-u 'username:API_TOKEN'` + - Header: `X-API-Token: API_TOKEN` + - Query: `?token=API_TOKEN` + - Request headers: - `Content-Type: application/json` - Request body: @@ -69,6 +100,7 @@ curl -i -X POST http://localhost:8080/bookmarks \ List bookmarks in reverse chronological order, or search with full-text. When `q` is provided, results include an `excerpt` with highlighted matches. The endpoint can return JSON (default) or RSS. +- Auth: required to see private bookmarks (session cookie or API token). If unauthenticated, only public bookmarks are returned. - Query params: - `limit` (optional, default 50, max 200) - `offset` (optional, default 0) @@ -78,13 +110,13 @@ List bookmarks in reverse chronological order, or search with full-text. When `q - `200 OK` with JSON array of bookmarks or RSS feed - `400 Bad Request` for invalid `limit` or `offset` - `500 Internal Server Error` on unexpected errors -- curl examples: +- curl examples (session or token required for private data): ``` -curl -s http://localhost:8080/bookmarks | jq -curl -s 'http://localhost:8080/bookmarks?limit=20&offset=20' | jq -curl -s 'http://localhost:8080/bookmarks?q=example' | jq -curl -s 'http://localhost:8080/bookmarks?format=rss' | xmllint --format - -curl -s 'http://localhost:8080/bookmarks.xml?q=example' | xmllint --format - +curl -s -u 'username:API_TOKEN' http://localhost:8080/bookmarks | jq +curl -s -u 'username:API_TOKEN' 'http://localhost:8080/bookmarks?limit=20&offset=20' | jq +curl -s -u 'username:API_TOKEN' 'http://localhost:8080/bookmarks?q=example' | jq +curl -s -u 'username:API_TOKEN' 'http://localhost:8080/bookmarks?format=rss' | xmllint --format - +curl -s -u 'username:API_TOKEN' 'http://localhost:8080/bookmarks.xml?q=example' | xmllint --format - ``` Notes on search excerpts: @@ -95,6 +127,7 @@ Notes on search excerpts: Update a bookmark’s URL, title, and/or description. +- Auth: required (session cookie or API token) - Request headers: - `Content-Type: application/json` - Request body: @@ -121,6 +154,7 @@ curl -s -X PUT http://localhost:8080/bookmarks/1 \ Delete a bookmark (and its search index entry). Archived text is removed from search index but remains in `archives` table only if you retain rows; this implementation removes the FTS entry, and the foreign key setup will cascade archives removal if configured via migrations. +- Auth: required (session cookie or API token) - Responses: - `204 No Content` on success - `404 Not Found` if the bookmark ID does not exist @@ -130,13 +164,31 @@ Delete a bookmark (and its search index entry). Archived text is removed from se curl -i -X DELETE http://localhost:8080/bookmarks/1 ``` +### Per‑user feeds + +Public JSON/RSS feeds are available at `/{username}.json` and `/{username}.rss`. + +- Unauthenticated or non‑owner requests return only public bookmarks. +- Owner (session, Basic auth, header, or query token) sees all bookmarks (public + private). + +Examples (include private when authenticated): +- Basic: `curl -u 'dce:API_TOKEN' http://localhost:8080/dce.rss` +- Header: `curl -H 'X-API-Token: API_TOKEN' http://localhost:8080/dce.json` +- Query: `curl 'http://localhost:8080/dce.json?token=API_TOKEN'` + +### Public JSON listing + +- `GET /u/{username}` → JSON array of that user’s public bookmarks (always public‑only). + ## Data Model Table: `bookmarks` - `id` INTEGER PRIMARY KEY AUTOINCREMENT (starts at 1) -- `url` TEXT NOT NULL UNIQUE +- `user_id` INTEGER NOT NULL REFERENCES `users(id)` +- `url` TEXT NOT NULL (unique per user: `(user_id, url)`) - `title` TEXT NOT NULL - `description` TEXT NULL +- `public` INTEGER NOT NULL DEFAULT 0 - `created_at` TEXT NOT NULL (RFC3339) - `updated_at` TEXT NOT NULL (RFC3339) @@ -148,6 +200,7 @@ An `archives` table stores the plaintext dump per bookmark and is kept in sync o Return the archived plaintext for the given bookmark ID. +- Auth: not required (returns plain text if the archive exists) - Responses: - `200 OK` with `text/plain; charset=utf-8` - `404 Not Found` if no archive exists for the bookmark