Repository navigation
DROP 1.0.0
·
346 commits
to develop
since this release
[1.0.0] - 2026-08-07
First public release. DROP now runs tenant apps under either PM2 or Docker
container isolation behind one runtime interface, exposes itself to coding
agents through a hosted MCP server with OAuth 2.1, and adds the guardrails,
monorepo support, and managed services needed to let an agent deploy safely
and unattended. This section also carries everything shipped but never
previously published since 0.1.0.
Security
- Access control: ownership enforcement on app mutation and log
endpoints (PUT /apps/:nameaccepts only a safe field allowlist — no more
userId/pathoverwrites); every deploy path (upload, git, agent
tooling) contains itself inside the webapps directory via a realpath
check that defeats symlink/junction/..escapes. - Multi-tenant isolation: a tenant-authored group or domain name can no
longer collide with, delete, or route-hijack another owner's app; a
colliding database name is refused instead of silently reused; deleting
an app now purges its logs and retained deploy artifacts instead of
leaving them readable by the next owner of that name; monorepo
materialization no longer lets one service's build escape into a
sibling's directory, and a dangling symlink can no longer squat a child
app's name. - Auth & API keys: authentication is on by default (
DROP_DISABLE_AUTH=true
to disable); JWT verification pinned to HS256; legacy password hashes are
compared in constant time and upgraded to scrypt on login;/auth/signup
is rate-limited; an API key's standing now derives from its owner instead
of the key being its own principal (which had let a suspended owner's
keys keep working, and orphaned apps/quotas); suspension and password
resets are reversible and contained rather than destructive; the
users:createcapability can no longer be escalated into arbitrary code
execution. - Agent & MCP surfaces: the untrusted-output fence around tenant-
controlled text — which stops a deployed app's text from acting as a
prompt injection against a model reading it — can no longer be forged or
bypassed; the MCPforward_authguard in front of a tenant's own MCP
endpoint now actually rejects every credential class except an
app-audienced bearer, instead of admitting others; per-app OAuth
audiences stop one app's token from reaching another app's MCP endpoint;
agent tokens carry an explicit scope grammar and are admitted narrowly at
the deploy gate. - Guardrails: closed several bypasses a dedicated security review found
in the agent-deploy guardrails — the circuit breaker and per-principal
quotas were inert on the exact code path an autonomous deploy loop rides,
and the idle reaper's dry-run budget was being spent by no-op sweeps
instead of real ones. - Build isolation: tenant build commands no longer inherit platform
secrets, on both the host and containerized build paths;install.shno
longer lets root execute drop-authored code, and the bundled Postgres
gets its own hardened, dedicated socket directory. - Webhooks: GitHub webhook signature verification no longer skips when
the header is omitted, guardsJSON.parse, and length-checks before
timingSafeEqual; outbound webhook URLs reject localhost/private/
link-local targets (SSRF). - Secrets: app secrets are encrypted with a standalone
encryption.key
(orDROP_MASTER_KEY) instead of a key derived from the store itself;
existing stores migrate transparently. - Misc: CORS defaults to same-origin (
DROP_CORS_ORIGINSto allowlist);
a Content-Security-Policy is set; git branch names are validated;
webhooks.jsonis0600; 500 responses no longer leak internal error
text.
Added
- Install from a published release.
install.sh --from-releasedownloads
the prebuilt bundle attached to a GitHub release, verifies its SHA-256 before
extracting anything, and installs without agit cloneor any TypeScript or
Vite build on the target machine. It requires an explicit--isolation=docker
or--isolation=noneon a first install, because that choice decides whether
tenant apps run in containers or as the system user that owns the platform's
encryption key — a one-line install command should not pick that silently.
Node.js, PostgreSQL and Caddy are provisioned for you; no C toolchain is
needed, since the last native dependency was removed. Every release also
attachesinstall.shand both checksums as individual assets, and the
landing page, documentation and README link them directly, so the bundle can
be fetched and inspected by hand before anything runs as root. - Docker isolation mode.
AppRuntimeis a formal seam with two
implementations — PM2 (isolation: none, the default) and Docker
containers (isolation: docker) — chosen once at boot from
config.isolation/DROP_ISOLATION. Container builds run in ephemeral,
non-root containers; static/SPA apps are served by an unprivileged nginx
with zero capabilities; Postgres has its own container-mode topology; live
stats and log streaming work the same way under both runtimes.drop migrate-runtimemoves an existing app between the two. - Hosted MCP server + OAuth 2.1.
POST /api/v1/mcp(PRD-040) exposes
DROP's own deploy/status/logs tools to Claude and other MCP clients.
OAuth 2.1 with PKCE (PRD-041) authorizes claude.ai's web connector,
including per-app MCP audiences and arevocation_endpoint. - Agent-deploy guardrails. A circuit breaker trips on a failing deploy
loop and resets on the first success; per-principal and per-owning-user
deploy quotas cap throughput regardless of outcome; ephemeral, TTL'd apps
(default 60 minutes, promotable to permanent) give agents a safe scratch
space; an idle reaper tears down abandoned agent-created apps; a per-app
disk ceiling blocks a deploy before it exhausts the box. Every limit
returns a structured refusal instead of a silent kill. - Monorepo / multi-service deploys. A
services:block indrop.yaml
expands one repository into N apps sharing a hostname, with
browser-reachabledepends_onURLs, same-origin/apirouting, and
group-aware start/stop/teardown/redeploy. - Managed Redis (PRD-050). A bundled Redis server provisions a per-app
logical database and injectsREDIS_URLfor apps that opt in via
drop.yaml. - Public site, docs, and reference — split from the dashboard (DROP-070).
/,/docs(PRD-043) and/reference(PRD-044) build as a separate
bundle from the authenticated/dashboardSPA, so a marketing visitor's
download never carries admin-only code. - Database panel. The dashboard's App Details page can browse an app's
provisioned database, reading it as the app's own database role rather
than an admin credential. - Multi-user MCP connectors. Non-admin users can set up their own
claude.ai connector; an admin-controlleduserConnectorsEnabledplatform
setting gates whether the capability is offered at all. - Agent-native deploy tooling: tarball upload deploys
(POST /apps/:name/source, PRD-039); scoped agent tokens
(POST /auth/agent-tokens) with a stable principal identity per caller;
a structured result for every deploy — a real error code, build-failure
classification from the log tail, andGET /deploys/:deployId/
get_deploy_logsto see why a specific deploy failed, with logs retained
past teardown. - Required secrets preflight (PRD-051). A deploy with
drop.yaml
secrets:missing is parked in aneeds-configstatus instead of
crash-looping, surfaced in both the API and dashboard. - Boot reconciliation. A platform restart no longer rebuilds every app
on the box; already-running apps are reconciled against their config
instead of redeployed. DROP_API_URL+ scopedDROP_API_KEY. Apps an admin grants
control-plane capabilities can call DROP's own REST API from inside their
own container/process with a least-privilege key, instead of needing the
admin key.- Auth: opt-in TOTP two-factor authentication; forced password change on
first login; admin-manageable GitHub webhook secret with a reveal-once
flow in the dashboard's Git settings tab. - CLI:
drop restorereversesdrop backup;drop backupnow also
captures every per-app database, not just platform state. - Dashboard: a log viewer (Runtime/Build tabs, stdout/stderr filter,
search with highlighting, pause/resume, copy/download, severity
color-coding, ANSI sanitization); settings reorganized into tabs (System /
Account / Activity / About) with the active tab kept in the URL; a
deploy-timeline panel and app-level Metrics tab (CPU/mem/uptime); a
redesigned auth flow, app shell, and design system; session-expiry
handling, a 404 page, logout redirect, an app-limit indicator
(GET /api/v1/usage), and a signup-success notice. - Continuous integration (GitHub Actions): lint, server build, tests, and
both dashboard builds on every PR tomain/develop. - Atomic, crash-safe writes (temp + fsync + rename) for every JSON/YAML
state store; a corrupt store is quarantined instead of silently wiped. .env.example, a LICENSE file, and afiles/prepublishOnlypackage
config.
Changed
drop serve -dnow applies the--root/--domain/--https/...flags it
forwards (previously ignored).- Boot recovery: apps whose process died while marked
runningare set to
pending(and restarted by the startup scan) instead ofstopped. - Version —
/health, the CLI,drop version, and the dashboard — is read
frompackage.jsoneverywhere, replacing several hardcoded, stale
version strings. - Dashboard assets are served with immutable cache headers;
index.htmlis
no-cache. - Git redeploy (API + webhook) always triggers a rebuild+restart after a
successful pull, including no-change pulls, and onboards a freshly cloned
app deterministically instead of waiting on the file watcher. getAppRuntime()returns whichever runtime is already active instead of
defaulting to PM2, so a caller can no longer accidentally target the
wrong adapter.
Fixed
- Deploy pipeline:
build:completedcarries asuccessflag and the
platform no longer starts an app after a failed build; the
appsInProgressguard no longer leaks (which had permanently dead-ended
hot reload). - Process safety:
unhandledRejection/uncaughtExceptionhandlers and a
bounded, guarded shutdown;waitForStatusthrows on timeout; build
commands hard-timeout and kill the process tree; app logs are tail-read
(no OOM on multi-GB files). - Caddy stderr/unexpected exit is logged at warn and surfaced via
platform:errorinstead of being swallowed at debug; a Caddy-rejected
config is no longer misreported as "Caddy not running". - The readiness gate no longer marks a healthy, slow-starting app as
errored. - Static apps now serve their built output directory instead of their
source root. - Resolved all ESLint errors; activity logging consolidated behind a
best-efforttryLogActivityhelper.