npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@restora/cli

v0.8.0

Published

Local, privacy-first scheduled Notion backups to your own storage — Google Drive, S3 (AWS/Backblaze B2/Cloudflare R2/Wasabi/DO Spaces), or a local folder. Runs on your machine — nothing is stored on any server.

Readme

Restora CLI — scheduled Notion backups to your own storage

Local, privacy-first automatic backups. The CLI runs on your machine, holds your credentials locally (~/.restora/config.json, owner-only), and streams your Notion workspace straight into a destination you own — Google Drive, any S3-compatible bucket (AWS, Backblaze B2, Cloudflare R2, Wasabi, DigitalOcean Spaces), or a plain local folder. No Restora server is ever involved — there is nothing on our servers to leak.

Why this design:

  • Notion internal-integration token (not OAuth) → it never expires, so scheduled backups never hit the 30-day OAuth reconnect wall, and the token never leaves your machine.
  • You own the destination → Google Drive uses the drive.file scope (the tool only sees files it created); S3 uses your own bucket + keys; local is just a folder on disk. Nothing is stored by us.

For users — one command

npx @restora/cli setup

The wizard walks you through it: connect Notion → share your databases → connect Google Drive (one click) → pick a daily time → first backup. Re-run anytime to change things. Prefer it installed? npm i -g @restora/cli, then restora setup.

Getting stale behavior? npx caches packages aggressively and can keep running an old build even after a new version is published — flags silently ignored, old output formats. Force the current release with npx @restora/cli@latest …, or if installed globally, npm i -g @restora/cli@latest.

Other commands:

restora status                          # what's connected, schedule, last backup
restora backup                          # back up now → Google Drive
restora backup --databases id1,id2      # back up a subset (remembered for scheduled runs)
restora backup --all                    # back up everything, and widen scheduled runs to everything
restora scope                           # what scheduled runs currently cover
restora scope --restore                 # undo the last --all widening, back to your saved subset
restora backup --to local --dir ./bk    # back up to a local folder instead
restora connect s3                      # connect your own S3-compatible bucket (see below)
restora backup --to s3                  # back up to your bucket
restora backup --keep 30                # keep only the newest 30 backups
restora backup --keep-weeks 8           # also delete backups older than 8 weeks (newest is always kept)
restora schedule --daily 02:00 | --remove
restora guard                           # snapshot BEFORE letting an AI agent loose (see below)
restora guard --report                  # exactly what that agent changed

Faster repeat backups: --incremental

Re-download only what changed since your last backup. Rows Notion reports as untouched are reused from your previous file; everything else is fetched fresh.

restora backup --incremental                 # uses your most recent local backup as the base
restora backup --incremental --from prev.json

Restora refuses to reuse anything it can't fully vouch for: if a database's schema changed, or the prior file came from a different backup format, that database is downloaded in full. False positives (a needless re-download) are fine; a missing row never is.

A backup that had to skip anything will not delete older backups, whatever your --keep setting — retention should never trade a complete backup for a partial one.

If a row can't be re-read from Notion, the copy from your previous backup is kept rather than dropping the row — the run tells you which rows those are, and that they may be out of date.

Fixed in 0.5.1 — please re-run any incremental backup made with an earlier version. Before 0.5.1, --incremental could attach the wrong file to a row it carried over from the previous backup (attachments were re-keyed per run, and the keys collided). Text, properties, blocks and relations were never affected, and the file itself still validates — restora verify passes, because the reference resolves; it just resolves to a different attachment. A fresh restora backup (full, no --incremental) produces a correct file. Full backups, the web app, and scheduled cloud backups were never affected.

Inspect your backups (offline, read-only)

Point these at your backup files — the newest in your local folder by default, or any file with --file <path>. All run entirely on your machine, make no network calls, and never change anything. They return proper exit codes, so they drop straight into CI or a pre-commit hook.

restora doctor                          # one-shot health check: connections, latest backup, integrity, freshness
restora validate                        # is this a well-formed backup file?  (exit 1 if not)
restora verify                          # integrity: files present, decode cleanly, relations intact  (exit 1 if not)
restora stats                           # counts: databases, rows, views, relations, files
restora inspect                         # duplicate rows/databases, broken rollups, detached pages
restora tree                            # the workspace hierarchy at a glance
restora search "launch email"           # full-text search across titles, properties, and page content
restora diff old-backup.json new-backup.json   # what changed between two snapshots (databases, rows, schema)

Same pure engines as the web app and the MCP server — just printed to your terminal.

Before you let an AI agent loose: restora guard

AI agents — Notion AI, MCP-connected assistants, bulk-edit scripts — can now edit and delete across your whole workspace in seconds. guard is the seatbelt:

restora guard --label "before-cleanup-agent"    # full snapshot, kept out of your normal backup history
# ... let the agent run ...
restora guard --report                          # rows added/removed/EDITED, schema changes, pages — offline

The report names exactly what changed (row and page titles, property changes), and both snapshots are full backups — if the agent broke something, restore the "before" file at app.restora.cc. For pipelines: --json prints the diff as JSON, and --fail-on-changes exits 1 when anything changed (CI gate for "this automation must not touch Notion").

Migrate in, export out (free, local)

restora import-obsidian ./MyVault --parent <notion-page-url>   # Obsidian vault → Notion, images included
restora export-md <notion-page-url> --out page.md              # any Notion page → clean Markdown
restora export-md <url> --images download                      # ...with images saved alongside
restora export-comments <notion-page-url>                      # every comment on a page → Markdown
restora export-comments <url> --format csv                     # ...or a spreadsheet-ready CSV

All of these run entirely on your machine with your own token — migration is never paywalled.

export-comments walks the whole block tree, so inline comment threads on individual blocks are included, not just page-level ones — each thread is grouped and labeled with the text it's attached to. Two Notion API limits, stated up front: only unresolved comments come back (resolved threads are unreachable by every tool), and your integration needs the "Read comments" capability (notion.so/profile/integrations → Capabilities — the command tells you if it's missing). Add "Read user information" too if you want real author names instead of ids.

Bring your own bucket (S3-compatible)

restora connect s3 connects AWS S3, Backblaze B2, Cloudflare R2, Wasabi, or DigitalOcean Spaces — no Restora setup required, you just supply your own bucket + keys (stored locally only, never sent to us). It asks for the provider, region/endpoint, bucket, and an access key/secret (use a key scoped to just that bucket: PutObject, ListBucket, DeleteObject). Then restora backup --to s3. Note: on S3, retention (--keep / --keep-weeks) permanently deletes old backups — enable bucket versioning if you want recoverability.

Use your backups from any AI agent (MCP)

restora mcp runs a local, read-only MCP server that lets any MCP-capable AI agent — Claude Desktop, Cursor, Cline, Continue, Zed, ChatGPT desktop, and more — read the backups already on your machine. Ask "what was in my Projects database last week?", "map my whole workspace", or "what changed since this backup?" and the agent answers by calling the server's tools.

Privacy by design: Restora makes no LLM calls, holds no API key, and — in its default mode — makes zero network calls. Your own agent does the reasoning over local JSON. Every tool is read-only, so even if a backup contains malicious text it can't trigger any action. (The store-nothing posture, but stronger.)

Get the config snippet to paste into your agent:

restora mcp --print-config

Then add it to your agent (same shape everywhere):

{ "mcpServers": { "restora": { "command": "npx", "args": ["-y", "@restora/cli", "mcp"], "env": {} } } }

By default it reads your local-backup folder (~/Restora Backups, or whatever you set). Point it elsewhere with --dir <folder> or --file <backup.json>.

Tools exposed: list_backups, describe_backup (workspace map: databases, properties, relations, views), query_database (rows with readable values; relations resolved to the linked page titles), get_page (a page as Markdown), search, and read_id_map (old→new ids after a restore, to repoint integrations/webhooks).

Live drift audit (opt-in): add --allow-live to also expose run_drift_audit, which compares a backup against your current Notion (deleted/emptied databases, removed properties, type changes) — the input for a restore plan. This is the only tool that uses the network, and only to your own Notion (it needs restora connect notion); it never calls an LLM or a Restora server.

Very large workspaces (memory)

Backups stream to disk as they're fetched — each database and file is written out and released, so even multi-GB workspaces don't need a big heap (upload destinations briefly use ~the backup's size in temp disk while shipping). The CLI also raises Node's heap automatically for backup as belt-and-suspenders. If a truly enormous single database still runs out of memory:

restora databases                      # list your databases + their ids
restora backup --databases id1,id2     # back up a batch; repeat for the next

Note: a relation from a database in one chunk to a database in another chunk won't resolve across separate files — group related databases into the same run where you can.

Scope is sticky — and reversible

--databases/--pages are remembered, so unattended scheduled runs use them too. --all does the opposite: it clears the saved subset, so scheduled runs go back to covering everything. That is usually what you want, but it outlives the run you typed it on, so the CLI now says exactly what it cleared and keeps it:

restora scope              # what scheduled runs cover right now
restora scope --restore    # put back the subset the last --all cleared

Behind a company proxy or firewall?

Node.js ignores your browser's and your system's proxy settings, so Notion can open fine in your browser while every command-line tool fails with a bare fetch failed. Restora reads the standard variables — set one and re-run:

set HTTPS_PROXY=http://proxy.company.com:8080          # Windows (cmd)
export HTTPS_PROXY=http://proxy.company.com:8080       # macOS / Linux

HTTP_PROXY and NO_PROXY are honoured too, and credentials can be embedded (http://user:pass@host:8080). If your company inspects TLS (Zscaler, Netskope and similar), also point Node at its root certificate with NODE_EXTRA_CA_CERTS=/path/to/company-root.pem — never disable certificate checking, since that would expose the token you're about to send.

When a connection fails, Restora probes DNS, TCP and TLS and prints which one broke rather than repeating Node's fetch failed. Full guide: https://restora.cc/docs/cli-network.

Limitations (stated honestly)

  • Scheduled backups run only when this machine is on and awake at the scheduled time.
  • Scheduling writes a small runner to ~/.restora and registers it with the OS scheduler (Windows Task Scheduler / cron). Logs go to ~/.restora/backup.log.
  • Files up to 150MB per file are bundled by default. The CLI stores nothing and cannot know your Restora plan, so it uses the Pro cap for everyone — tune it in either direction with the FILE_MAX_BYTES environment variable (bytes, e.g. FILE_MAX_BYTES=20971520 for 20MB). A skipped file is named in the run's warnings and stays safe in Notion — but it is not in the backup: the reference kept alongside it is Notion's expiring signed URL, dead within about an hour. Restoring any file over 20MB needs the destination Notion workspace on a paid Notion plan (Notion's multi-part upload limit; free Notion workspaces accept uploads only up to 5MB).