# lnkdwn.com — full reference (llms-full.txt) > Linkdown — turn any Markdown into a shareable link. Operated by 0451 > Software. The short index is at [/llms.txt](https://lnkdwn.com/llms.txt). > Format spec for this file: https://llmstxt.org. ## 1. Site map | Path | Auth | Purpose | |---------------------------------------|-----------|------------------------------------------------------| | `/` | none | Marketing home (served from this dashboard app) | | `/cli` | none | CLI install instructions + one-liner | | `/sign-in` | none | Clerk sign-in / sign-up (hosted by Clerk) | | `/dashboard` | Clerk | Authenticated dashboard home (deck list) | | `/dashboard/billing` | Clerk | Subscription management (links to Stripe portal) | | `/d/{token}` | none | Public share viewer (iframe renders the deck) | | `/pricing` | none | Pricing tier table + comparison | | `/privacy` | none | Privacy policy (full text) | | `/terms` | none | Terms of service (full text) | | `/docs` | none | Markdown docs index (forthcoming — tracked in plan) | | `/install.sh` | none | Bash installer for the Rust CLI | | `/llms.txt` | none | This short index | | `/llms-full.txt` | none | This file | | `/openapi.yaml` | none | OpenAPI 3.0.3 spec for the worker API | | `/.well-known/security.txt` | none | RFC 9116 security disclosure contact | | `/security.txt` | none | Redirects to `/.well-known/security.txt` (RFC 9116) | The dashboard itself is a Next.js 16 app deployed to Cloudflare Pages via OpenNext. The worker API runs on Cloudflare Workers (Hono.js, Node compat). The static content under `/privacy`, `/terms`, `/pricing`, `/llms.txt`, `/llms-full.txt`, and `/.well-known/security.txt` is prerendered at build time (`pnpm --filter dashboard build`) and served as static HTML or text. ## 2. Authentication ### 2.1 Browser (Clerk) The dashboard uses Clerk for hosted sign-in / sign-up. The dashboard server components gate on `currentUser()` from `@clerk/nextjs`. The worker's `/me`, `/decks`, `/shares`, `/billing/portal`, and `/storage-usage` endpoints accept a Clerk session JWT in `Authorization: Bearer `. The worker validates the token against Clerk's Backend API JWKS endpoint (cached). The dashboard re-signs the bearer on every request from the Clerk session cookie. ### 2.2 CLI (RFC 8628 device flow + RFC 7636 PKCE) `linkdown login` opens the browser to a Clerk sign-in page tied to a one-shot user code. Wire flow: 1. `POST /auth/device/code` → `{ device_code, user_code, verification_uri, expires_in, interval, code_challenge, code_challenge_method }` 2. User opens the verification URI, types the user code, signs in. 3. CLI polls `POST /auth/device/token` with the device code + PKCE verifier every `interval` seconds. Returns `{ access_token, token_type: "Bearer", expires_in }`. 4. The CLI stores the access token in the OS keyring (macOS Keychain / Linux secret-service / Windows Credential Manager) under `linkdown-access-token`. The PKCE verifier is stored under `linkdown-pkce-verifier` (cleared on logout). 5. The CLI refreshes the token before expiry (no refresh-token rotation: the device flow re-issues a fresh access token on 401 + retry). `linkdown logout` deletes both keyring entries. ## 3. CLI reference (`linkdown` Rust binary) Source: `apps/cli/src/main.rs`. README: `apps/cli/README.md`. ### 3.1 Global flags - `--api-base ` — override the API base (default: `https://api.lnkdwn.com`). - `--json` — emit machine-readable JSON on stdout (for agents). - `--no-color` — disable ANSI colour. - `--verbose` / `-v` — increase log verbosity. ### 3.2 Commands #### `linkdown login` Opens browser to the device-flow verification URI and polls for the access token. Stores credentials in the OS keyring. #### `linkdown logout` Removes both keyring entries (`linkdown-access-token` and `linkdown-pkce-verifier`). Idempotent. #### `linkdown whoami` `GET /me` with the stored bearer. Prints: email, Clerk user id, internal `userId`, `tier`, `storageLimitBytes`, `storageUsedBytes`, `deckLimit`. With `--json`, emits the raw worker response. #### `linkdown create [PATH]` 1. Auto-detect the framework at `PATH` (default: `.`). 2. Run the framework's build command: - Slidev: `npx slidev build` - Marp: `npx @marp-team/marp-cli` - Reveal.js: no build step (zip the static output directory). 3. Zip the build output. 4. `POST /decks` → `{ id, uploadUrl, storageKey }`. 5. `PUT ` with the zip (Content-Type: `application/zip`). 6. `POST /decks/{id}/complete` → finalises; the worker HEADs the R2 object, checks `sizeBytes` against the tier cap, fires the thumbnail worker, and returns the row. 7. `POST /decks/{id}/share` (default link) → prints the share URL. Flags: - `--name ` — explicit deck name (default: directory basename). - `--framework ` — override auto-detection. - `--no-share` — skip the default share link creation. - `--expiry ` (paid tiers only) — `1h`, `24h`, `7d`, `30d`. - `--password ` (Pro+) — bcrypt-hashed server-side. - `--view-limit ` (Pro+) — integer 1..10000. #### `linkdown update ` 1. `GET /decks` → find the deck by name. 2. `PUT /decks/{id}` with `{ replaceFile: true }` → returns a fresh presigned URL pointing at the deck's existing `storageKey`. 3. Re-zip the local build output and PUT it. 4. `POST /decks/{id}/complete` → re-fires the thumbnail worker. 5. Existing share URLs are preserved (the `storageKey` is reused, so `/d/{token}` keeps serving the new file). Flags: same as `create` minus `--name` (taken positionally) and `--framework` (inferred from the existing deck row). #### `linkdown list` `GET /decks?limit=50` (paged via `?cursor=...`). Prints: name, internal id, status (`processing` / `ready` / `failed`), size, slide count, created/updated timestamps, share URL (if a default link exists). With `--json`, emits the full `decks[]` array. #### `linkdown delete ` `GET /decks` → find by name → `DELETE /decks/{id}`. Cascades to `shares` (FK cascade) and `storage_usage`. The deck object in R2 is purged by a daily cron (`apps/workers/src/cron.ts`, runs at 03:00 UTC). The CLI does NOT wait for the cron — it returns as soon as the DB row is gone. #### `linkdown share ` Creates or rotates a share link. `POST /decks/{id}/share` with the optional `expiresInDays` / `password` / `viewLimit` body. Prints the share URL. Free tier: returns 403 `TIER_LIMIT_REACHED` if any Pro-only field is set. Pro+ tier: the server hashes the password with bcrypt (cost 12) and stores the hash + the plain-text `viewLimit`. #### `linkdown unshare ` `DELETE /decks/{id}/share` (bulk revoke). The default share link is deleted; the underlying deck row is unchanged. Re-running `share` without `--no-share` regenerates a fresh token. ### 3.3 Framework detection rules | Framework | Detection signal | |-------------|---------------------------------------------------------------------------| | Slidev | `package.json` has `slidev` in `scripts` or `dependencies` | | Marp | `@marp-team/marp-cli` in deps, OR `.marp/` config dir, OR `_mp.md` files | | Reveal.js | `index.html` has a `