TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Roam Publish 的網站與 API(將 Roam Research 頁面發佈到網路)
AI 翻譯
Website and API for Roam Publish (publish Roam Research pages to the web)
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Server and website for Roam Publish, a Roam Research extension that publishes
pages and blocks to the web.
For how the server and extension work together (the contract, shared invariants, trust boundary, and how to ship
changes across both), see roam-publish-docs.
Without RESEND_API_KEY, verification and reset emails are printed to the server console.
Edit src/db/app-schema.ts (or re-run bunx auth@latest generate --config src/lib/auth.ts --output src/db/auth-schema.ts
after changing better-auth plugins), then bun run db:generate and commit the migration in drizzle/.檢視完整 README ↗Server and website for Roam Publish, a Roam Research extension that publishes pages and blocks to the web.
For how the server and extension work together (the contract, shared invariants, trust boundary, and how to ship changes across both), see roam-publish-docs.
Stack: Bun · Next.js (App Router) · shadcn/ui (Blueprint-styled) · better-auth (+ api-key plugin) · Drizzle · Postgres · Resend.
bun install
cp .env.example .env.local # fill in BETTER_AUTH_SECRET and APPEND_TOKEN_KEY (openssl rand -base64 32 each)
bun run db:migrate
bun dev
Without RESEND_API_KEY, verification and reset emails are printed to the server console, except in production,
where they aren't sent and an error is logged instead (their links must never reach the host's logs).
Emails are Resend templates, defined in src/lib/email-templates.ts with the layout in src/lib/email-layout.ts.
There's nothing to set up in Resend: the first time each one is sent, the server publishes it as a template whose
alias ends in a hash of its content (rp-verify-email-1a2b…), so a changed email is a new template and staging and
production never overwrite each other's. If Resend's templates fail, the same email is sent as plain HTML instead.
Old rp-* templates can be deleted from the Resend dashboard once nothing sends them.
Admins read and answer email sent to the domain at /admin/inbox. It lists what Resend's receiving API holds (so
receiving must be turned on for the domain in Resend) and needs no other setup; replies go out from EMAIL_FROM with
MODERATION_REPLY_TO as their Reply-To, and are logged in the inbox_reply table.
bun run test
Unit tests (tests/unit) need nothing. Integration tests (tests/integration) run against a real Postgres: they
create and migrate TEST_DATABASE_URL (default postgres://postgres@localhost:5433/roam_publish_test) and empty it
before each test, so never point it at a database you care about. Next's request APIs (headers, cookies, after,
cache revalidation) are stubbed in tests/helpers/preload.ts; Roam's Append API and email are never called.
A test.failing is a known bug: it passes while the bug is there, and fails once it's fixed, so swap it for test.
CI runs typecheck, lint and the tests on every PR.
develop first. Railway's staging environment (the shadow database) deploys it. Its pre-deploy step (bun run staging:check) runs the full test suite against a throwaway roam_publish_test database on the staging Postgres, then migrates staging. Wait for that deploy to succeed.develop into main.develop is always the staging branch for main.Edit src/db/app-schema.ts (or re-run bunx auth@latest generate --config src/lib/auth.ts --output src/db/auth-schema.ts
after changing better-auth plugins), then bun run db:generate and commit the migration in drizzle/.
Tags and search text are derived from each page's tree on publish (src/lib/tags.ts). After deploying migration
0018_search_and_tags, or after changing how tags are extracted, run railway run bun run search:backfill once so
existing pages get them.
/discover/feed.xml: always on, the newest pages on Discover./{graph}/feed.xml: off by default; owners turn it on in graph settings. Needs the front page on and open to everyone./c/{slug}/feed.xml: off by default; owners turn it on in collection settings. Needs the collection page open to everyone.Feed readers send no cookies, so feeds only ever list pages open to everyone (see src/lib/feeds.ts).
Graph owners' append-only tokens are stored AES-256-GCM encrypted with APPEND_TOKEN_KEY (src/lib/append-token.ts)
and only used to append the change log under shortlink blocks. Without the key, nothing is stored and the change log
is off.
Rotating the key (e.g. it leaked, but the database didn't):
APPEND_TOKEN_KEY_PREVIOUS to the old key and APPEND_TOKEN_KEY to a new one (openssl rand -base64 32),
then deploy. Tokens encrypted with either key keep working.railway run bun run tokens:rotate to re-encrypt every token with the new key.APPEND_TOKEN_KEY_PREVIOUS and deploy.If the key and the database may both have leaked, treat the stored tokens as exposed: set a new key, deploy, and
run railway run bun run tokens:revoke-all. Every owner gets a dashboard banner asking for a new token; tell them to
revoke the old one in Roam (Settings → Graph → API tokens). An append-only token can only add blocks to its own graph.
Changing the key without tokens:rotate doesn't break anything: tokens the server can no longer read are marked
invalid the next time they're needed, and owners are asked for a new one.
A page can be encrypted when every place it's shown is Password (src/lib/encryption.ts, rules in
src/lib/encryption-rules.ts). Its tree is encrypted with a content key, and that key is sealed to each password's
X25519 public key; each private key is stored wrapped under scrypt(password). Readers' browsers unlock with a proof
derived from the password and decrypt the page themselves (src/lib/reader-crypto.ts, src/server/actions/unlock.ts),
so rendering never decrypts on the server.
encryption_version 1): the server encrypts a plain tree on arrival (sealNewContent). Extensions before
0.2.0, Roam without X25519, and the dashboard's encrypt switches make these.GET /api/ext/publications/:rootUid/seal for the public keys, encrypts in Roam and
sends only the cipher and sealed keys (src/lib/e2e-publish.ts). The server never sees the text./privacy/encryption and /privacy/encryption/versions explain both to readers; add a version there (and in
ENCRYPTION_VERSIONS) whenever the format or who runs it changes. The extension's src/seal.ts must match
encryption.ts and reader-crypto.ts byte for byte.
The extension sends its version (x-roam-publish-version, from 0.2.0), recorded per person and graph in ext_client
and shown at /admin/extension; the extension-versions job emails admins once everyone active in the last 30 days
is on the version Roam Depot serves. Every /api/ext response names the oldest extension this site works with
(x-roam-publish-min-version, EXT_MIN_VERSION in src/lib/ext-compat.ts), and older extensions from 0.2.0 ask
people to update.
/api/ext changes are additive; removing something older extensions rely on goes through /updates/upcoming
(src/lib/upcoming.ts) and a major release (see CLAUDE.md, Extension compatibility). bun run db:migrate, the
production pre-deploy step, runs scripts/ext-gate.ts first and fails the deploy while anyone active in the last 30
days is on an extension older than EXT_MIN_VERSION. Elsewhere it only warns.
CHANGELOG.md is what people see at /updates, next to the extension's changelog at the commit Roam Depot serves
(src/lib/whats-new.ts). Every user-facing change adds a Breaking:, New:, Improved: or Fixed: bullet; tests
and the Changelog check enforce the format and the version bump (see CLAUDE.md).
Owners delete a graph in its settings, and themselves at the bottom of the dashboard. Deleting an account goes
through better-auth's deleteUser with an email confirmation; src/lib/deletion.ts deletes everything first.
Deleting can't be a way out of a moderation action:
blocked_identity blocklist, and its suspended collections keep their slugs. Sign-up, graph verification
and username claims check the blocklist. Admins lift entries at /admin/blocked.Every route handler, server action and call to an outside service (Roam's Append API, Umami, Resend) is timed by
src/lib/telemetry.ts; page render errors come in through onRequestError in src/instrumentation.ts. Counts,
errors and a latency histogram are kept per minute in memory, saved to endpoint_metric by the metrics-flush job
and kept two weeks.
/admin/status: health, then calls, error rate, p50/p95/max and the last error for each entry point over
the last hour, day or week. Rows past 2% errors or a slow p95 (2 s, 5 s for outside services) are flagged.GET /api/health: 200 {"status":"ok"} while the database answers and the job worker has written a
heartbeat in the last three minutes, else 503 {"status":"degraded","checks":{...}}. Point Railway's
healthcheck or an uptime monitor at it.409 ×38) next to errors, not as errors. A route where
over a quarter of 20+ calls are refused is flagged too: that's usually our bug, like the content hash drifting.status-alerts job checks the last 15 minutes every 5 minutes and emails a digest when
something starts failing, a reminder every 6 hours while it lasts, and an all-clear when it's fixed. It counts
3+ errors over 2% of calls, a slow p95 over 10+ calls, a mostly-refused route, or a failing, overdue or stalled job.
It goes to every user with better-auth's admin role; ALERTS=off stops it. It needs the
database and Resend, so pair it with an uptime monitor on /api/health for outages.{"level":"error","metric":"GET /api/search","ms":…,"error":…}); search Railway's logs with @metric:….A new route handler must wrap each method in withRoute(...), and a new server action its body in
return withAction(...); tests/unit/entry-points.test.ts fails otherwise. A thrown error or 5xx is a failure;
an { ok: false } result or a 4xx isn't. Page timings aren't recorded: Railway's HTTP metrics have them.
See docs/api-contract.md, and roam-publish-docs for the reasoning behind it.
This is a third-party service made by @ejqs. Not affiliated with Roam Research.
The code is licensed under the MIT License.
The site icon (🌍) and header logo (🌍📝) are from Twemoji, © Twitter, Inc
and other contributors, licensed under CC BY 4.0. They are used
unmodified: the icon is rendered to PNG and ICO in src/app/, and the logo images are in public/emoji/.