
Security News
When Autonomous Agents Escape: Why Socket Signed the Cyber Defense Open Letter
Socket joins more than 100 technology, cybersecurity, and financial organizations calling for a global surge in cyber defense.
@dropthis/cli
Advanced tools
![]()
Official CLI for dropthis -- the publish layer between AI and the internet. Built for humans, AI agents, and CI/CD pipelines.
npm install -g @dropthis/cli
Requires Node.js >= 20.
dropthis login
dropthis ./index.html # publish is the default command
Log in with email OTP:
dropthis login
The CLI prompts for your email and a one-time code. For non-interactive environments (agents, CI), use the two-step flow:
# Step 1: Request OTP
dropthis login request --email you@example.com
# Step 2: Verify OTP
dropthis login verify --email you@example.com --otp 123456
A failed verify prints
Your code has expired(otp_expired) orThat code is incorrect(otp_invalid) and exits non-zero.
Keys are minted with a capability bundle. The default for dropthis login is publish — enough to publish drops and read your own context, nothing else. To manage a team you need a wider bundle, requested with --scope on login verify:
| Bundle | Grants | Use it for |
|---|---|---|
publish (default) | publish + read your own account/workspaces + mint downscoped keys | publishing, CI |
team | publish + create/rename workspaces, invite/manage members | running a team |
team-admin | team + delete workspaces, remove members, change roles | irreversible team admin |
service | publish without key-minting (workspace-pinned) | automation / CI that can't mint keys |
# Mint a team-scoped key
dropthis login verify --email you@example.com --otp 123456 --scope team
Minting is downscope-only — a minted key can never have more scopes than the key that minted it. Check what your current key carries:
dropthis whoami --json # echoes the key's scopes
If a command returns insufficient_scope (exit 1, HTTP 403), your key lacks a scope that route needs. Fix it by re-logging in with the right bundle (dropthis login verify … --scope team) or, via the SDK, apiKeys.create({ scopes: ["team"] }).
Credentials resolve in this order:
--api-key sk_... flagDROPTHIS_API_KEY environment variabledropthis loginIf you run dropthis publish without credentials in an interactive terminal, the CLI prompts you to log in inline — no separate dropthis login step needed. Disable with --no-interactive.
dropthis whoami # Check current auth status
dropthis logout # Remove stored credentials
dropthis logout --revoke # Remove and revoke the key on the server
publish is the default command — you can omit it and pass files directly:
# These are equivalent:
dropthis ./report.html
dropthis publish ./report.html
# HTML file
dropthis ./report.html
# Directory (static site)
dropthis ./dist
# Stdin
echo "<h1>Hello</h1>" | dropthis publish - --content-type text/html --path index.html
# Multiple files bundled into one drop
dropthis index.html styles.css app.js
# Print only the URL (recommended for agents)
dropthis ./dist --url
# Publish by reference — the server fetches the URL, you never handle the bytes
dropthis publish --source-url https://example.com/report.html
--source-url)--source-url <url> is the explicit flag form of the positional-URL publish — same behavior as dropthis publish https://…, but discoverable and unambiguous in scripts. The server fetches the http(s) URL and serves the result; the bytes never pass through your process.
dropthis publish --source-url https://example.com/report.html
ftp://, a malformed URL, or an empty value all fail with invalid_usage ("source_url must be an http(s) URL.").dropthis publish --source-url https://x ./file.html is rejected).--manifest — manifest files carry their own per-file source_url instead.--title, --domain, --dry-run, etc.) exactly like the positional URL form.dropthis publish ./dist \
--title "Launch page" \
--visibility unlisted \
--entry index.html \
--expires-at 2026-12-31T00:00:00Z
publish always creates a NEW drop with a new URL. To change a drop you already published, use update-content or update-settings with its drop_… id — never publish again.
Every drop has a canonical url — an always-branded human view (badge guaranteed). For a single non-HTML file (.pdf, .png, .json, a URL you asked the server to fetch, etc.), publish also prints a Raw: line: the file's exact bytes at a natural path, no wrapper. Hand the canonical url to people; hand the raw URL to agents or downstream tooling that needs the real bytes.
--manifest)When an AI agent generates an HTML page that references external images or other files, use --manifest to publish everything as one drop without base64-encoding the assets. The server fetches remote files during publish so bytes never pass through your process.
dropthis publish --manifest bundle.json
bundle.json shape:
{
"files": [
{
"path": "index.html",
"content": "<html>...</html>"
},
{
"path": "hero.jpg",
"source_url": "https://cdn.example.com/hero.jpg"
},
{
"path": "data.json",
"content_base64": "eyJrZXkiOiJ2YWx1ZSJ9"
}
]
}
Each file entry must have a path and exactly one content key:
| Key | Description |
|---|---|
content | UTF-8 text content (HTML, CSS, JSON, markdown, …) |
source_url | Public https:// URL — the server fetches the bytes server-side. Use this for images and other binary assets instead of base64-encoding them. |
content_base64 | Base64-encoded bytes (for binary files you already have in memory) |
Optional per-file keys: content_type (MIME type override).
The --manifest flag also works on update-content:
dropthis update-content drop_abc123 --manifest bundle-v2.json
Cannot be combined with a positional file/folder/URL argument.
All publish flags:
| Flag | Description |
|---|---|
--title <title> | Drop title |
--visibility <public|unlisted> | Drop visibility |
--password <password> | Require a password to view the drop (Pro plan) |
--noindex | Prevent search indexing |
--slug <vanity-slug> | Vanity slug for a path-mode custom domain (only with --domain; Pro) |
--domain <hostname> | Serve the drop under one of your connected custom domains (Pro) |
--shared | Publish to the shared dropthis pool, bypassing your default custom domain |
--workspace <slugOrId> | Target a specific workspace for this publish (delegated keys) |
--entry <path> | Entry file for directories |
--content-type <mime> | MIME type (required for stdin) |
--path <path> | Filename (required for stdin) |
--manifest <file> | Multi-file bundle JSON (see above) |
--source-url <url> | Publish by reference: server fetches this http(s) URL (see above). Cannot combine with a positional input or --manifest |
--expires-at <datetime> | Expiration datetime |
--metadata <json> | Metadata as JSON string |
--metadata-file <path> | Metadata from a JSON file |
--idempotency-key <key> | Idempotency key |
--no-optimize | Disable server-side image optimization (byte-exact upload) |
--url | Print only the URL, nothing else |
--json | Output full JSON response |
--dry-run | Validate without publishing |
Oversized png/jpeg/webp images are optimized server-side by default (re-encoded smaller; paths never change), disclosed via warnings[] in the response — pass --no-optimize to opt out and upload byte-exact.
These flags are inherited by all commands:
| Flag | Description |
|---|---|
--api-key <key> | Override API key for this invocation |
--api-url <url> | Override API base URL |
--json | Force JSON output (implies non-interactive — never prompts) |
-q, --quiet | Suppress status output and imply JSON |
--no-interactive | Disable interactive prompts (inline auth, confirmations) |
--insecure-store | Store credentials in a plaintext file instead of the OS keyring, for headless/CI boxes without a keyring backend (also DROPTHIS_INSECURE_STORE=1) |
Change an existing drop without creating a new URL. Content and settings are separate operations — pass the full drop_… id (not the slug):
# Replace the content at the same URL (ships a new deployment)
dropthis update-content drop_abc123 ./dist-v2
# Update from a manifest bundle (same --manifest format as publish)
dropthis update-content drop_abc123 --manifest bundle-v2.json
# Change settings only — title, visibility, password, expiry, metadata
dropthis update-settings drop_abc123 --title "New title"
# Optimistic concurrency (both accept --if-revision)
dropthis update-content drop_abc123 ./dist-v2 --if-revision 1
update-content updates the files at the URL — patch by default: files you don't include are carried forward, so you can ship a single changed file. Use --mode replace (or --replace) for a full content swap, and --delete-path <path> (repeatable) to remove a file. Like publish, update-content optimizes oversized images by default — pass --no-optimize for a byte-exact upload. update-settings changes title, visibility, password, noindex, expiry, or metadata — including --no-password to clear a password and --index to re-allow indexing. Creating a new drop is always publish — neither update command makes a new URL.
With --if-revision, a concurrent edit fails with a 409 instead of clobbering. The error shows the drop's current_revision and how to retry:
✗ Revision mismatch
The drop changed since it was last read. Re-read it with GET /v1/drops/{drop_id} and retry with if_revision: 2 (sent as the If-Revision header).
Current revision: 2 — retry with --if-revision 2
In JSON mode the same error carries current_revision and a next_action field.
Download what a drop is serving — by drop_… id, drop URL, or slug. URLs and slugs are resolved to your own drops; the local copy is also the rollback path (pull an old state, then update-content it back):
# By id, into ./drop_abc123/
dropthis pull drop_abc123
# By URL — resolves the slug to your drop, writes into ./abc123/
dropthis pull https://abc123.dropthis.app
# Choose the output directory
dropthis pull drop_abc123 -o ./site
Pull fetches the current deployment's file manifest and writes every file into the output directory. It is owner-side read-back via the API — it works regardless of any viewer password. Custom-domain URLs are not resolvable yet; use the drop_… id instead.
Like pull, but reads to stdout instead of writing files to disk — for scripts and agents that want the manifest or a single file's exact bytes without a local directory. Accepts the same target forms: drop_… id, drop URL, or slug.
# File manifest (paths, content types, sizes) — no --path
dropthis get-content drop_abc123
# One file's exact bytes to stdout — pipeable, redirectable
dropthis get-content drop_abc123 --path index.html > index.html
# Read a historical deployment instead of the current one
dropthis get-content drop_abc123 --deployment dep_x --path index.html > old.html
--path)Manifest mode prints JSON to stdout, but the exact shape depends on output mode:
Human mode (TTY) — the bare manifest, pretty-printed, nothing else on stdout:
{
"dropId": "drop_abc123",
"deploymentId": "dep_xyz",
"revision": 3,
"status": "active",
"sizeBytes": 4821,
"entry": "index.html",
"files": [
{ "path": "index.html", "contentType": "text/html", "sizeBytes": 4821 }
]
}
JSON mode (piped / --json / CI / --quiet) — a single-line CLI envelope, consistent with every other command:
{"ok":true,"content":{"dropId":"drop_abc123","deploymentId":"dep_xyz","revision":3,"status":"active","sizeBytes":4821,"entry":"index.html","files":[{"path":"index.html","contentType":"text/html","sizeBytes":4821}]}}
Pipes and agents always get the envelope (non-TTY stdout auto-selects JSON mode), so jq addresses .content.files:
dropthis get-content drop_abc123 --json | jq -r '.content.files[].path'
--path <file>)--path <file> writes that file's exact stored bytes to stdout — no encoding round-trip, no trailing newline, nothing else on stdout. Safe to redirect straight to a file:
dropthis get-content drop_abc123 --path index.html > index.html
--json conflicts with --path — file bytes are written raw, so wrapping them in a JSON envelope isn't possible. Combining both is invalid_usage, exit 2.invalid_usage: "refusing to write binary (content-type … ) to a terminal"). Pass --force to override, or just redirect/pipe (agents and pipes are never a TTY, so this never blocks a script).dropthis get-content … --path x > out never writes a partial or error-shaped file.--deployment <deploymentId>Both modes accept --deployment <deploymentId> to read from a specific past deployment instead of the current one — the same flag as pull. Useful for inspecting or diffing an older version without rolling back.
Publishes land in a workspace. Everyone has a personal workspace; teams add shared ones. List the workspaces you belong to and switch the active one (delegated keys):
dropthis workspace list
dropthis workspace use acme # by slug or ws_… id
Creating and managing team workspaces needs a team-scoped key (see Scopes):
dropthis workspace create "Acme" --slug acme # create a team workspace
dropthis workspace rename ws_team123 --name "Acme Inc"
dropthis workspace delete ws_team123 --yes # team-admin scope
Manage who's in a workspace:
dropthis members list ws_team123
dropthis members invite ws_team123 --email teammate@acme.com --role member # --role admin|member
dropthis members role ws_team123 acc_123 --role admin # --role owner|admin|member
dropthis members remove ws_team123 acc_123 --yes
Accept an invitation you received:
dropthis invitations list # pending invitations for your email
dropthis invitations accept --token inv_tok_abc # accept by invite token
dropthis invitations accept-by-id inv_123 # or by invitation id
To publish into a specific workspace for one call, pass --workspace <slugOrId> on publish (delegated keys only).
dropthis [input...] # Publish content (default command)
dropthis publish [input...] # Same as above, explicit form
dropthis update-content <drop-id> [input] # Update a drop's content (patch by default; same URL)
dropthis update-settings <drop-id> # Change title/visibility/password/expiry/metadata
dropthis pull <id|url|slug> [-o <dir>] # Download a drop's files to a local directory
dropthis get-content <id|url|slug> # Read a drop's file manifest, or one file's bytes, to stdout
dropthis get <id|url|slug> # Show drop details
dropthis resolve <id|url|slug> # Resolve a drop URL/slug back to its drop_… id
dropthis list # List your drops
dropthis list --domain reports.example.com # Only drops on a custom domain
dropthis delete <id|url|slug> # Delete a drop (--yes to confirm)
dropthis analytics <drop-id> # Show a drop's view analytics (Keep+ counts, Pro breakdowns)
dropthis deployments list <drop-id> # List a drop's deployment history
dropthis deployments get <drop-id> <dep-id> # Show one deployment
dropthis deployments restore <drop-id> <dep-id> # Roll a drop back to a prior deployment (Pro+)
dropthis domains connect <host> --mode <path|dedicated> # Connect a custom domain (Pro)
dropthis domains list # List custom domains
dropthis domains status <host> # Show domain + DNS status
dropthis domains verify <host> [--wait] # Verify DNS (exit 6 pending, 7 timeout)
dropthis domains update <host> [--default|--drop <id>] # Repoint or set the default domain
dropthis domains remove <host> --yes # Disconnect a domain
dropthis workspace list # List workspaces you belong to
dropthis workspace use <slug|id> # Switch active workspace (delegated keys)
dropthis workspace create <name> --slug <s> # Create a team workspace (team scope)
dropthis workspace rename <ws> --name <name> # Rename a team workspace (team scope)
dropthis workspace delete <ws> --yes # Delete a team workspace (team-admin scope)
dropthis members list <ws> # List a workspace's members
dropthis members invite <ws> --email <e> --role <admin|member> # Invite someone (team scope)
dropthis members role <ws> <account-id> --role <owner|admin|member> # Change a member's role
dropthis members remove <ws> <account-id> --yes # Remove a member (team-admin scope)
dropthis invitations list # List pending invitations for your email
dropthis invitations accept --token <token> # Accept an invitation by token
dropthis invitations accept-by-id <inv-id> # Accept an invitation by id
dropthis login # Authenticate with email OTP
dropthis login request --email <email> # Request OTP (non-interactive)
dropthis login verify --email <email> --otp <code> # Verify OTP
dropthis logout # Remove stored credentials
dropthis whoami # Show current auth status
dropthis account # Show account details, active tier + limits
dropthis account update --display-name <name> # Update your display name
dropthis account delete --yes # Delete your account
dropthis api-keys create [--service --workspace <ws>] # Mint a delegated key, or a pinned CI service key
dropthis api-keys list # List API keys
dropthis api-keys delete <key-id> --yes # Delete an API key
dropthis upgrade # Update the CLI to the latest version
dropthis doctor # CLI diagnostics
dropthis commands # Machine-readable command metadata
The CLI is designed for non-interactive use. In non-TTY environments (pipes, CI, agents), output defaults to JSON automatically.
Environment variables:
export DROPTHIS_API_KEY=sk_live_... # API key (same as --api-key)
export DROPTHIS_API_URL=https://... # Override API base URL (same as --api-url)
export DROPTHIS_NON_INTERACTIVE=1 # Disable interactive prompts (same as --no-interactive)
export DROPTHIS_INSECURE_STORE=1 # Plaintext credential file instead of the OS keyring (same as --insecure-store)
export DROPTHIS_NO_UPDATE_NOTIFIER=1 # Disable the startup update notice (also auto-disabled in CI, non-TTY, --json/--quiet)
export NO_COLOR=1 # Disable ANSI color in human output (also auto-disabled when not a TTY)
dropthis publish ./dist --url
Agent protocol:
--url to get only the published URL (cleanest for agents)--json for the full structured response--yes for destructive commands (delete, api-keys delete, account delete)--no-interactive to disable inline auth promptsnext_action fieldExit codes:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | API or generic error |
| 2 | Invalid usage |
| 3 | Auth required (no credential, or the API returned 401 — re-authenticate) |
| 4 | Local input error (file or directory not found, too many files) |
| 5 | Network error (could not reach the API) |
| 6 | Domain verification pending (domains verify one-shot, not live yet) |
| 7 | Domain verification timeout (domains verify --wait exceeded --timeout) |
This table — plus the global flags (--api-key, --api-url, --json, --quiet, --no-interactive, --insecure-store) — is machine-readable from dropthis commands --json (the exit_codes and global_options fields), so an agent can discover the whole surface without scraping docs.
Some failures share exit 1 but carry a distinct error code in the JSON envelope:
| Error code | HTTP | Meaning & fix |
|---|---|---|
insufficient_scope | 403 | The key lacks a scope this route needs. Re-login with the bundle (dropthis login verify … --scope team), or mint a key with it via the SDK. See Scopes. |
quota_exceeded | 403/413 | A plan limit was hit. Check dropthis account --json (entitlements.limits vs usage) or upgrade. |
JSON output shape:
{"ok":true,"drop":{"url":"https://abc123.dropthis.app/","id":"drop_abc123"}}
Error shape (stderr):
{"ok":false,"error":{"code":"auth_error","message":"No API key found.","next_action":"Set DROPTHIS_API_KEY or run dropthis login."}}
Run diagnostics to verify CLI health:
dropthis doctor
Reports CLI version, the bundled @dropthis/node SDK version, auth source (env, flag, storage, or missing), credential storage backend (secure, insecure, or none), and — for a stored key — its capability scopes.
{"ok":true,"version":"0.38.0","sdk_version":"0.34.0","auth":{"source":"storage"},"storage":{"backend":"secure"},"scopes":["team"]}
Add --online for a network preflight an agent can run before a publish to tell apart the common failures — re-auth vs network vs an unresolved workspace:
dropthis doctor --online --json
It hits GET /v1/account (token valid? which workspace resolves? which plan?) and lists custom-domain verification status as a checks array. It exits 3 on an auth failure and 5 on a network failure, so a script can branch on the exit code.
For a pre-publish quota check, dropthis account --json returns entitlements.limits (e.g. maxStorageBytes, maxCustomHostnames, seatLimit) alongside your current usage — read it before a large publish to avoid a quota_exceeded failure.
dropthis has four tiers — Free, Keep, Pro, and Business. Free drops carry the dropthis badge and expire after 30 days; paid tiers add permanence, larger limits, custom domains, passwords, and analytics. Run dropthis account to see your active tier and its exact caps, or visit https://dropthis.app/pricing for the current plans.
Connecting a custom domain (dropthis domains connect) requires the Pro plan (1 hostname). Domains already connected keep working regardless of plan.
dropthis account shows your active tier, its limits, and the workspace your key is bound to. If the workspace kind is team, your publishes land under the team's shared custom domain automatically — no extra flags needed.
For AI coding agents (Cursor, Claude Code, Windsurf, etc.), install the dropthis-skills package:
npx skills add dropthis-dev/dropthis-skills
FAQs
Official CLI for Dropthis.
The npm package @dropthis/cli receives a total of 248 weekly downloads. As such, @dropthis/cli popularity was classified as not popular.
We found that @dropthis/cli demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Socket joins more than 100 technology, cybersecurity, and financial organizations calling for a global surge in cyber defense.

Product
Enterprise security teams can now detect malware, credential theft, suspicious network activity, and risky updates across Microsoft Edge extensions.

Research
/Security News
Socket researchers found 18 Chrome extensions and one Edge extension delivering a wallet drainer, credential theft, and other malicious payloads.