{"_id":"@dwmkerr/livedown","_rev":"3-d2915ae53997606d0d4db32f5f8f1057","name":"@dwmkerr/livedown","dist-tags":{"latest":"0.1.7"},"versions":{"0.1.4":{"name":"@dwmkerr/livedown","version":"0.1.4","keywords":["markdown","live","preview","collaborative","partykit","websocket"],"author":{"name":"Dave Kerr"},"license":"MIT","_id":"@dwmkerr/livedown@0.1.4","maintainers":[{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"}],"homepage":"https://github.com/dwmkerr/livedown#readme","bugs":{"url":"https://github.com/dwmkerr/livedown/issues"},"bin":{"livedown":"dist/cli.js"},"dist":{"shasum":"bc8e4e8088beba6fcda26c8f894aff1fa5d2e0ad","tarball":"https://registry.npmjs.org/@dwmkerr/livedown/-/livedown-0.1.4.tgz","fileCount":12,"integrity":"sha512-08CLd78OPfw+XiqKkKevLjf+AbKuBNIM+7xyPJ/khHIqY8dbcCbBeEKD6pElnjN92PwcBrBWib3fk4hMDNi9NQ==","signatures":[{"sig":"MEYCIQCwkaBR1Y8ycRrCdoPQB5AX/E8UNaLV/rJZ/rJ4DeVHzgIhAJoRhetiWk4/BNFs1HNXEs9yKpgHehLePvRiZDpf/E6N","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dwmkerr%2flivedown@0.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":88595},"main":"./dist/cli.js","type":"commonjs","gitHead":"caa368a24296cdd895448b8dbacf1ac3314a0a6f","scripts":{"dev":"tsx watch src/cli.ts","lint":"eslint .","test":"jest --no-coverage --testPathIgnorePatterns=integration","build":"tsc && chmod +x dist/cli.js","start":"ts-node ./src/cli.ts","prepare":"husky","lint:fix":"eslint --fix .","test:all":"jest --no-coverage","test:cov":"jest --coverage --testPathIgnorePatterns=integration","build:watch":"tsc -w","test:integration":"jest --no-coverage --forceExit --testPathPatterns=integration","test:integration:watch":"LIVEDOWN_OPEN_BROWSER=1 jest --no-coverage --forceExit --testPathPatterns=integration"},"_npmUser":{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"},"repository":{"url":"git+https://github.com/dwmkerr/livedown.git","type":"git"},"_npmVersion":"11.12.1","description":"Share a local markdown file and collaborate live in a browser and across machines.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.19.0","open":"^11.0.0","chokidar":"^4.0.0","commander":"^11.1.0","tweetnacl":"^1.0.3","clipboardy":"^5.3.1","gray-matter":"^4.0.3","@noble/curves":"^2.0.1","@inquirer/prompts":"^8.3.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","jest":"^30.3.0","husky":"^9.1.7","eslint":"^9.39.4","ts-jest":"^29.4.6","ts-node":"^10.9.2","partykit":"^0.0.115","@types/ws":"^8.5.10","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.5","typescript-eslint":"^8.57.2","eslint-plugin-prettier":"^5.0.1"},"_npmOperationalInternal":{"tmp":"tmp/livedown_0.1.4_1776812145278_0.6624760129900018","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@dwmkerr/livedown","version":"0.1.5","keywords":["markdown","live","preview","collaborative","partykit","websocket"],"author":{"name":"Dave Kerr"},"license":"MIT","_id":"@dwmkerr/livedown@0.1.5","maintainers":[{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"}],"homepage":"https://github.com/dwmkerr/livedown#readme","bugs":{"url":"https://github.com/dwmkerr/livedown/issues"},"bin":{"livedown":"dist/cli.js"},"dist":{"shasum":"51395c382597ff2cd450fd9ee7def610a18da740","tarball":"https://registry.npmjs.org/@dwmkerr/livedown/-/livedown-0.1.5.tgz","fileCount":12,"integrity":"sha512-W2KHPILiE0hO2B7ZJtOdz1C/TYNPS2C1Ifa/1g1eThoi9NFuAx6yssLeo6eloxHe4Qqgd4TUD1KRnsj8jC4jWQ==","signatures":[{"sig":"MEUCIQCgtEMcTXGU48o08N16fV1qNVkTjx+naIx75F+0ZGXF7gIgad//wSh28DM/B6jiRKIdoW/+PiuuFQrM/o/D2gnXy9Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dwmkerr%2flivedown@0.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":115677},"main":"./dist/cli.js","type":"commonjs","gitHead":"f081e840505dc52a0c1e42b97d2c4f2e61c51db2","scripts":{"dev":"tsx watch src/cli.ts","lint":"eslint .","test":"jest --no-coverage --testPathIgnorePatterns=integration","build":"tsc && chmod +x dist/cli.js","start":"ts-node ./src/cli.ts","prepare":"husky","lint:fix":"eslint --fix .","test:all":"jest --no-coverage","test:cov":"jest --coverage --testPathIgnorePatterns=integration","build:watch":"tsc -w","test:integration":"jest --no-coverage --forceExit --testPathPatterns=integration","test:integration:watch":"LIVEDOWN_OPEN_BROWSER=1 jest --no-coverage --forceExit --testPathPatterns=integration"},"_npmUser":{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"},"repository":{"url":"git+https://github.com/dwmkerr/livedown.git","type":"git"},"_npmVersion":"11.11.0","description":"Share a local markdown file and collaborate live in a browser and across machines.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.19.0","open":"^11.0.0","chokidar":"^4.0.0","commander":"^11.1.0","tweetnacl":"^1.0.3","clipboardy":"^5.3.1","gray-matter":"^4.0.3","@noble/curves":"^2.0.1","@inquirer/prompts":"^8.3.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","jest":"^30.3.0","husky":"^9.1.7","eslint":"^9.39.4","ts-jest":"^29.4.6","ts-node":"^10.9.2","partykit":"^0.0.115","@types/ws":"^8.5.10","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.5","typescript-eslint":"^8.57.2","eslint-plugin-prettier":"^5.0.1"},"_npmOperationalInternal":{"tmp":"tmp/livedown_0.1.5_1777982903304_0.03244786870163563","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"@dwmkerr/livedown","version":"0.1.7","description":"Share a local markdown file and collaborate live in a browser and across machines.","type":"commonjs","main":"./dist/cli.js","bin":{"livedown":"dist/cli.js"},"scripts":{"build":"tsc && chmod +x dist/cli.js","build:watch":"tsc -w","dev":"tsx watch src/cli.ts","relay:dev":"partykit dev --port 1999","start":"ts-node ./src/cli.ts","lint":"eslint .","lint:fix":"eslint --fix .","test":"jest --no-coverage --testPathIgnorePatterns=integration","test:integration":"jest --no-coverage --forceExit --testPathPatterns=integration","test:integration:watch":"LIVEDOWN_OPEN_BROWSER=1 jest --no-coverage --forceExit --testPathPatterns=integration","test:all":"jest --no-coverage","test:cov":"jest --coverage --testPathIgnorePatterns=integration","prepare":"husky"},"repository":{"type":"git","url":"git+https://github.com/dwmkerr/livedown.git"},"bugs":{"url":"https://github.com/dwmkerr/livedown/issues"},"homepage":"https://github.com/dwmkerr/livedown#readme","keywords":["markdown","live","preview","collaborative","partykit","websocket"],"author":{"name":"Dave Kerr"},"license":"MIT","publishConfig":{"access":"public"},"dependencies":{"@inquirer/prompts":"^8.3.2","@noble/curves":"^2.0.1","chokidar":"^4.0.0","clipboardy":"^5.3.1","commander":"^11.1.0","gray-matter":"^4.0.3","open":"^11.0.0","tweetnacl":"^1.0.3","ws":"^8.19.0"},"devDependencies":{"@types/jest":"^29.5.14","@types/node":"^20.11.5","@types/ws":"^8.5.10","eslint":"^9.39.4","eslint-plugin-prettier":"^5.0.1","husky":"^9.1.7","jest":"^30.3.0","partykit":"^0.0.115","ts-jest":"^29.4.6","ts-node":"^10.9.2","tsx":"^4.21.0","typescript":"^5.3.3","typescript-eslint":"^8.57.2"},"gitHead":"1ed998094f5e34336647fdaf125ac46b843656d4","_id":"@dwmkerr/livedown@0.1.7","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-kMxnN10qKFhIHv+Jhzfk8r5xwmYqCou4Acm+jSYLS2Ac7lw3YLCs57M0rwz+Pu2aveyq69eJ/E/R9dywa2KSrQ==","shasum":"6f5830c88d6b5775a99557f4fb09997600212571","tarball":"https://registry.npmjs.org/@dwmkerr/livedown/-/livedown-0.1.7.tgz","fileCount":14,"unpackedSize":157062,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dwmkerr%2flivedown@0.1.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHlFgwKw76WugI3c8dZ6EGSTWqu97FXKB6O2+e7RJz6kAiA78rvHmsNlH7KrH9LtwBV+wrqGGkS0ypka76buhKnZ+Q=="}]},"_npmUser":{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"},"directories":{},"maintainers":[{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/livedown_0.1.7_1780480724116_0.3857556619028757"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T22:55:45.146Z","modified":"2026-06-03T09:58:44.584Z","0.1.4":"2026-04-21T22:55:45.436Z","0.1.5":"2026-05-05T12:08:23.471Z","0.1.7":"2026-06-03T09:58:44.328Z"},"bugs":{"url":"https://github.com/dwmkerr/livedown/issues"},"author":{"name":"Dave Kerr"},"license":"MIT","homepage":"https://github.com/dwmkerr/livedown#readme","keywords":["markdown","live","preview","collaborative","partykit","websocket"],"repository":{"type":"git","url":"git+https://github.com/dwmkerr/livedown.git"},"description":"Share a local markdown file and collaborate live in a browser and across machines.","maintainers":[{"name":"dwmkerr-admin","email":"dwmkerr@gmail.com"}],"readme":"<p align=\"center\">\n  <h2 align=\"center\"><code>📝 livedown</code></h2>\n  <h3 align=\"center\">Share a local markdown file and collaborate live in a browser and across machines.</h3>\n  <p align=\"center\">\n    <a href=\"https://livedown.dwmkerr.partykit.dev\">Site</a> |\n    <a href=\"#quickstart\">Quickstart</a> |\n    <a href=\"#commands\">Commands</a> |\n    <a href=\"#developer-guide\">Developer Guide</a> |\n    <a href=\"#security\">Security</a> |\n    <a href=\"#advanced\">Advanced</a> |\n    <a href=\"#how-it-works\">How It Works</a>\n  </p>\n\n  <p align=\"center\">\n    <a href=\"https://github.com/dwmkerr/livedown/actions/workflows/cicd.yaml\"><img src=\"https://github.com/dwmkerr/livedown/actions/workflows/cicd.yaml/badge.svg\" alt=\"cicd\"></a>\n    <a href=\"https://github.com/dwmkerr/livedown/actions/workflows/deploy.yaml\"><img src=\"https://github.com/dwmkerr/livedown/actions/workflows/deploy.yaml/badge.svg?event=workflow_dispatch\" alt=\"deploy\"></a>\n    <a href=\"https://github.com/dwmkerr/livedown/actions/workflows/openspec-flow.yaml\"><img src=\"https://github.com/dwmkerr/livedown/actions/workflows/openspec-flow.yml/badge.svg\" alt=\"openspec flow\"></a>\n    <a href=\"https://www.npmjs.com/package/@dwmkerr/livedown\"><img src=\"https://img.shields.io/npm/v/%40dwmkerr/livedown\" alt=\"npm version\"></a>\n    <a href=\"https://codecov.io/gh/dwmkerr/livedown\"><img src=\"https://codecov.io/gh/dwmkerr/livedown/graph/badge.svg\" alt=\"codecov\"></a>\n  </p>\n</p>\n\n## Quickstart\n\nRun `livedown share`:\n\n```bash\nnpx @dwmkerr/livedown share ./docs/architecture.md\n```\n\nThe CLI syncs the file to an ephemeral relay and provides a URL for others to view. An edit key can be shared to allow others to edit the file - changes will be synched to the local filesystem by the CLI.\n\n<p align=\"center\">\n  <img src=\"docs/terminal-share.svg\" alt=\"livedown share terminal output\" width=\"720\">\n</p>\n\nThe ephemeral relay will look similar to the below, and disappear when the CLI is terminated:\n\n<p align=\"center\">\n  <img src=\"docs/livedown-share-architecture-doc-browser-screenshot.png\" alt=\"livedown browser viewer showing the architecture doc\" width=\"900\">\n</p>\n\n## Commands\n\n### `livedown share <file>`\n\nWatch a local file and share it live.\n\n```bash\nlivedown share ./notes.md\n```\n\nOptions:\n- `-r, --relay <host>` — Relay host (default: `livedown.dwmkerr.partykit.dev`)\n- `-e, --editor <name>` — Your name shown to viewers\n- `-k, --edit-key <key>` — Edit key (auto-generated if omitted)\n- `-p, --private` — Require a view key to read the document (auto-generates one)\n- `--view-key <key>` — View key for private mode (auto-generated if omitted; implies `--private`)\n\nBy default anyone with the Join URL can read the document. With `--private`, the CLI prints a separate **view key** that viewers must enter before any content is delivered — a leaked URL reveals nothing. The edit key also grants view access, so editors need only one secret.\n\n## Developer Guide\n\nRun the full stack locally — relay, viewer, and CLI all on your machine, no deployed relay required. PartyKit serves the relay *and* `public/` on `http://localhost:1999` with caching disabled, so edits to `public/index.html` show up on browser refresh.\n\n```bash\n# Clone, install, build, link.\ngit clone git@github.com:dwmkerr/livedown.git\ncd livedown\nnpm install && npm run build && npm link\n\n# Terminal 1 — relay + viewer on localhost:1999.\nnpm run relay:dev\n\n# Terminal 2 — rebuild dist/ on save so `livedown` always runs latest source.\nnpm run build:watch\n\n# Terminal 3 — share a file against the local relay.\nPARTYKIT_HOST=localhost:1999 livedown --dev share ./README.md\n```\n\nThe CLI prints a `http://localhost:1999/#…` URL. Open it and iterate.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for pull request requirements.\n\n## Security\n\nLivedown writes remote content to your local disk, so every update is signed with **Ed25519** and verified by three independent layers before anything lands on your filesystem:\n\n- The **relay** rejects pushes without a valid signature.\n- The **watcher** on your machine re-verifies every incoming update before writing to disk.\n- The **browser** checks that any entered edit key matches the room's public key before it will send a push.\n\nThe URL is a locator, not a credential — it is safe to share. The edit key is the credential and must stay private.\n\n**Private mode** (`--private`) adds a **view key** that gates reading as well, so a leaked URL exposes no content. The view key is independent of the edit key, but the edit key also grants view access. View gating is enforced at the relay (the relay already holds content in plaintext); for confidentiality *from* the relay you would need end-to-end encryption, which livedown does not currently do.\n\nSee [docs/architecture.md](docs/architecture.md) for the full security model, including the keypair lifecycle and defense-in-depth table.\n\n## Advanced\n\n### Docker\n\nRunning Livedown in Docker isolates it from your local filesystem — only the directory you bind-mount is visible to the CLI. Useful as an extra security boundary when running untrusted documents.\n\n```bash\ndocker run --rm -v \"$(pwd):/data\" ghcr.io/dwmkerr/livedown share /data/notes.md\n```\n\nPass additional CLI flags after the image name:\n\n```bash\ndocker run --rm -v \"$(pwd):/data\" ghcr.io/dwmkerr/livedown share /data/notes.md \\\n  --editor \"Alice\" \\\n  --edit-key <your-edit-key>\n```\n\n> **Platform note:** The published image targets `linux/amd64`. ARM users (Apple Silicon, Raspberry Pi) should build locally: `docker build -t livedown-local .`\n\n## How It Works\n\n```\n Your Machine             Relay                    Collaborator\n ┌──────────┐            ┌──────────────┐          ┌──────────┐\n │ notes.md │───signed──▶│  Verify sig  │──update─▶│ Browser  │\n │          │◀──verify───│  Broadcast   │◀─signed──│          │\n └──────────┘            └──────────────┘          └──────────┘\n```\n\nLivedown is three pieces:\n\n1. A **CLI** on your machine watches your markdown file and pushes signed updates when it changes.\n2. A **relay** in the cloud forwards those updates to everyone connected to the same document.\n3. A **browser viewer** renders the document live, and — with the edit key — lets collaborators edit it back.\n\nDetails on the state machine, message protocol, security model, and the PartyKit / Cloudflare Workers relay setup live in [docs/architecture.md](docs/architecture.md).\n\n## OpenSpec Flow\n\nThis repo uses **OpenSpec Flow** — a GitHub Actions workflow that drives a Claude Code agent through the full OpenSpec lifecycle: issues become spec PRs, spec PRs become implementation PRs, and either can be refined on demand by adding the `openspec:start` label. See [`CLAUDE.md`](CLAUDE.md) for agent permission setup, secret rotation, and project conventions.\n\n### Troubleshooting\n\n- **Agent runs don't produce output, or produce very limited results.** Known issue with upstream [`anthropics/claude-code-action`](https://github.com/anthropics/claude-code-action/issues/874): if the Anthropic account's credit is too low or unavailable, the SDK ends the run after a short number of turns with an error that is not propagated up to the workflow. The step reports success, but no branch or PR is created — impl PRs contain only archived spec content, issue state flips to `openspec:review` with nothing to review. Mitigation: top up credit on [console.anthropic.com](https://console.anthropic.com/), or set `CLAUDE_CODE_OAUTH_TOKEN` to run against a Claude subscription instead of API billing. Tracked in [#98](https://github.com/dwmkerr/livedown/issues/98).\n\n- **Both `ANTHROPIC_API_KEY` and `CLAUDE_CODE_OAUTH_TOKEN` set at once.** The Claude Agent SDK prefers the API key (same precedence as the local `claude` CLI), so OAuth is ignored. Delete whichever secret you don't want to use. See [`CLAUDE.md` → Secrets](CLAUDE.md) for rotation notes and Anthropic's [authentication docs](https://code.claude.com/docs/en/authentication) / [consumer terms](https://www.anthropic.com/legal/consumer-terms) before using OAuth in CI — sanctioned for personal use on your own repo, but firing on third-party events ([#838](https://github.com/anthropics/claude-code-action/issues/838)) has led to account bans.\n\n## License\n\nMIT\n\n---\n\n<sub>UI prototyped on [Claude Design](https://claude.ai/design/p/68395513-244f-402c-b6ee-77499c42f583?file=Livedown+Designs.html). See [`design/`](design/) for artifacts, [`docs/design.md`](docs/design.md) for the implementation contract.</sub>\n\n<!-- openspec-flow install-start -->\n## openspec-flow\n\nThis repo uses [openspec-flow](https://github.com/dwmkerr/openspec-flow) to drive spec-driven development from GitHub issues.\n\n1. Open an issue describing the feature, fix, or task.\n2. Add the `openspec:go` label.\n3. openspec-flow opens a **spec PR** (`openspec:spec`). Review, comment, iterate (add `openspec:go` to the PR to re-run). Merge when happy.\n4. openspec-flow opens an **impl PR** (`openspec:impl`). Review, iterate, merge. The originating issue closes automatically.\n\nRequired Actions secret: `ANTHROPIC_API_KEY`.\n<!-- openspec-flow install-end -->\n","readmeFilename":"README.md"}