Repository navigation
Releases: Abhi-Subedi/LocalDrop
Release list
LocalDrop 1.1.2
LocalDrop 1.1.2
[1.1.2] — 2026-10-02
Two packaging faults made the released binaries unusable. Both passed CI,
passed localdrop --check, and were only found by installing the published
artefact and opening it. 1.1.1 was withdrawn within minutes of publication for
the first of them.
Fixed
- The web UI was never served. The SPA directory had two resolvers:
launcher._bundled()usedsys._MEIPASS, whilemain.SPA_DISTused
Path(__file__).parent / "static". PyInstaller points__file__at
<bundle>/localdrop/main.py, so the second looked in
<bundle>/localdrop/staticwhile the files ship in<bundle>/static.
SPA_DIST.exists()was therefore always False in a frozen build, so the
catch-all route and the/assetsmount were never registered:
GET /api/v1/versionreturned 200 andGET /returned 404.if spa.exists()made it silent.--checkmissed it precisely because it
already used the correct resolver. Both callers now share one implementation
inlocaldrop/paths.py, and a missing SPA logs an error at startup instead of
quietly degrading to 404s. - The Windows archive dropped the whole PyInstaller payload. The archive walk
was gated onexe.is_dir(), which is never true becauseexeis a file in
both build modes, so_internal/was omitted and the binary died with
Failed to load Python DLL .../_internal/python312.dll. The published zip had
3 entries where it should have had 803.
Added
- Regression tests for both, in the shape that failed: one builds a fake
<bundle>and assertsmain.SPA_DISTfinds the realindex.html, the other
builds a one-directory payload and asserts the archive contains it. Both were
verified to fail against the code that shipped.
Full changelog: https://github.com/Abhi-Subedi/LocalDrop/blob/v1.1.2/CHANGELOG.md
Upgrade notes: forward-only migrations. Take a backup first (docs/BACKUP.md).
LocalDrop 1.1.1
LocalDrop 1.1.1
[1.1.1] — 2026-10-02
Every install path was broken in the same way, and Windows could not start at
all. The one-liner installers advertised in the README resolved the release
version from raw.githubusercontent.com/<owner>/<repo>/VERSION, which is a URL
that cannot exist — the CDN needs a ref in the path. So irm ... | iex and
curl ... | sh failed for every user on the default path, on every platform,
and only worked if you happened to pass an explicit version. Fixing that
surfaced four more defects behind it, including one that made a completed
Windows install unable to start.
Fixed
- Both native installers failed to resolve a version (
install.ps1,
install.sh). The URL was missing the/mainref, so it 404'd and the
defaultstablepath never worked. Resolution now reads the releases API
first — wherelatestcannot name a version whose assets are missing — and
falls back toVERSIONonmain, because depending on a single host is the
underlying problem.GITHUB_TOKENis honoured to lift the 60/hour API limit.
A failure now names both URLs tried and how to pin a version, instead of a
bare404. - A native Windows install completed and then could not start. The embedded
PostgreSQL was unpacked withfilter="tar", which reproduces the archive's
0o700directory modes; on Windows that produces a protected DACL granting
onlyOWNER RIGHTS,SYSTEMandAdministrators, excluding the installing
user. 73 of 76 extracted directories were affected, soinitdbfailed with
could not access file ".../pg/share/postgres.bki": Permission denied.
localdrop --checkpassed because it never touches the database. Now uses
filter="data", which is also the safer filter. brew installcould not have worked. The advertised tap did not exist,
and the formula didpkgshare.install "LICENSE"/"README.md"for files
the tarballs never contained, which aborts the install. The tap is published
atAbhi-Subedi/homebrew-localdrop
and keeps itself current daily.linux-armv7was accepted but has never been built.install.shrequested
an artefact no release publishes, so 32-bit ARM boards failed later with an
unexplained download404.build-binary.pywas worse: it fell through to
labelling an armv7 build aslinux-x64. LocalDrop ships 64-bit only.update-brew-formula.py --check-onlycould never succeed. It validated
--sha256-*and then discarded them, so it always exited 2 before checking
anything, which is why the placeholder check was wrapped in|| trueand
never fired.- The Homebrew formula could not be re-versioned.
render()only replaced
placeholders, which are gone after the first release, so the formula stayed
pinned to whatever version it first reached.
Security
- The SPA catch-all route joins the request path onto the SPA directory, which
is not safe on its own: on pathlibbase / "/abs"discardsbaseentirely,
and a bare absolute path resolves to a real file outside the SPA. The
is_relative_toguard is correct and is now pinned by a test that fails when
the guard is removed. The three CodeQLpy/path-injectionalerts on this
function are answered by that test rather than dismissed on inspection.
Added
- LICENSE and README.md ship inside the release archives. AGPL-3.0 requires
conveying the licence with the binary, so this closes a compliance gap as well
as a packaging one. GOVERNANCE.md, stating the project's scope and decision-making plainly.- A pinned roadmap issue listing what still needs building, and what will not.
- Discussions, with a "start here" post.
- CodeQL analysis for Python, JavaScript/TypeScript, Ruby and Actions.
Full changelog: https://github.com/Abhi-Subedi/LocalDrop/blob/v1.1.1/CHANGELOG.md
Upgrade notes: forward-only migrations. Take a backup first (docs/BACKUP.md).
LocalDrop 1.1.0
LocalDrop 1.1.0
[1.1.0] — 2026-10-01
LocalDrop is now installable on every platform. 1.0.0 shipped a Docker-only
install that worked, but left a Linux server, a Mac, and a Windows desktop with
three different un-documented workarounds. This release closes that gap and makes
the repository releaseable as open source.
Added — installation
- One-line Linux/macOS installer.
curl -fsSL …/install.sh | shdownloads a
checksummed binary, verifies its SHA-256, creates a lockedlocaldropsystem
account, installs a systemd unit (Linux) or a launchd job (macOS), generates a
secret key, and starts the service.--no-serviceinstalls the binary alone;
your data and config are never overwritten. - Windows installer. A proper Inno Setup package (Start Menu and desktop
shortcuts, optional start-at-login, uninstaller) plus a portable zip of the
same payload. Both are checksummed. install.ps1for Windows: fetches, verifies, installs, and runs
localdrop --checkto prove the install works before it reports success.- Homebrew formula for macOS (
brew install abhi-subedi/localdrop) —
per-user, nosudo, no service auto-install. uninstall.shthat keeps your data by default and only deletes it when
you pass--purgeand confirm.- Self-contained installs need no database at all. When
LOCALDROP_DATABASE_URLis unset, the launcher provisions an embedded
PostgreSQL inside the data directory, so a native install is a single binary
with nothing else to install. Point it at your own PostgreSQL and the
embedded cluster is never started. - Frozen binaries for every platform —
linux-x64,linux-arm64,
macos-x64,macos-arm64,windows-x64— with a publishedSHA256SUMS. - Published container image on
ghcr.io/abhi-subedi/localdropfor
linux/amd64andlinux/arm64, tagged:1.1.0,:1.1,:stable,
:latest, with OCI labels and provenance attestation.
Added — operating the server
localdrop --checkverifies an install is self-sufficient and exits: every
bundled resource, the heavy C extensions (psycopg, argon2, Pillow) and the app
factory. It needs no database, so it works when the thing that is broken is
the database. The release pipeline runs it against every artefact before
publishing.localdrop --setup-tokenprints the one-time onboarding token on demand,
so a headless install does not have to dig it out ofjournalctl.localdrop install-service/uninstall-service/service-statusfor a
per-user macOS LaunchAgent — the launchd job Homebrew users need, since
Homebrew will not install one for them.localdrop-migrateapplies migrations programmatically, so the packaged
Alembic environment is found regardless of working directory. Docker, systemd,
the launcher and the frozen binary all use the same path.localdrop.envis read from the data directory when present, so every
platform is configured the same way. Real environment variables still win.GET /api/v1/versionreports the running build (public, and free of any
deployment detail). The Settings "About" panel reads it instead of showing a
hard-coded string./api/v1/health/readyreports the failing subsystem by name, and
LOCALDROP_DB_CONNECT_TIMEOUT/LOCALDROP_DB_KEEPALIVE_SECONDSbound how
long it can take to find out.
Added — working on the project
- Continuous integration (
.github/workflows/ci.yml): lint, types, the
test suite against real PostgreSQL 16, a frontend type-check and build, a
frozen-binary build that self-checks, and a Docker build that must serve
/api/v1/version. - Automated releases (
release.yml): tag a version and get binaries for
five platforms, a multi-arch image, checksums, a Release whose notes come
from this file, an updatedVERSIONfile, and a pushed Homebrew formula. - Nightly checks (
nightly.yml):pip-audit,npm audit, a Trivy scan of
the published image, CodeQL, and an end-to-end pass against the real
stableimage that fails if it is not the versionVERSIONclaims. - Dependabot, grouped by ecosystem so review is a handful of PRs, not
dozens. scripts/check_version.py,scripts/check_config_docs.pyand
scripts/check_fr_traceability.py— the doc-drift gates the spec asked
for and never had. Requirement-to-test traceability is now enforced, with the
remaining gaps registered and visible instead of assumed.SECURITY.md,SUPPORT.md, bug and feature issue templates, a PR
template,CODEOWNERS,.editorconfigand.gitattributes.
Fixed
- Readiness took 130 seconds to report a dead database. The engine had no
connect_timeout, so libpq waited its default ~2 minutes — longer than the
Docker healthcheck's 5-second timeout. Now ~5 seconds, with a regression test
that fails if the bound is removed. - Pool hygiene:
pool_recycleandpool_timeoutare set, and TCP keepalives
close half-open connections instead of letting them pin a pool slot. - Production builds shipped without any CSS.
index.csswas never imported
by the app entry, so the built SPA rendered unstyled. - A pip install could not run. The migrations and the built web UI lived
outside the package, so only the Docker layout — which copied them back in by
hand — worked. Both now ship inside the wheel, andpip install localdrop
and the frozen binary resolve them identically. docker compose pullcould not work. The compose file referenced a
locally built tag that was never published. It now pulls from GHCR, so the
documented upgrade path is real.
Changed
- One version, one source of truth.
__version__in
__about__.pyfeeds the wheel, the binary, the HTTP API, the Settings panel
and the release tooling. The three-way drift between1.0.0,1.1.0and
"unreleased" is no longer possible; CI fails if it comes back. - The repository's own quality gates now pass.
ruffandmypywere
reporting 230 and 37 problems respectively before this release. Both are clean. docs/CONFIGURATION.mdis complete — it was missing 15 settings.
Security
- Release artefacts are SHA-256 verified by every installer before anything is
unpacked, and releases carry a provenance attestation. - The systemd unit runs as a dedicated account with
NoNewPrivileges,
ProtectSystem=strictandReadWritePathslimited to the data directory. - Windows and macOS installs write the config file
0600; the launchd plist
holds the secret key and is not world-readable. - Reports go through private vulnerability reporting, not public issues.
Known limitations
- Single owner account; no multi-user sharing (use public links).
- Files live in folders; no folder or zip download.
- No live cross-device refresh (no SSE): the UI refreshes after your own
actions, and another device's changes appear on reload. - LAN discovery is URL + QR.
LOCALDROP_MDNS_ENABLEDis reserved and unused. - macOS Homebrew ships raw tarballs, not bottles: no
brew installof a
pre-built binary, so the formula builds from the release archive. - Windows binaries are unsigned. SmartScreen will warn on first run.
- 9 of 31 Must-have requirements have no automated test; they are listed with
reasons inscripts/check_fr_traceability.pyunderKNOWN_GAPS. - The embedded PostgreSQL is downloaded from Maven Central on first run and is
not checksum-verified. (Release artefacts are; this one is not yet.)
Full changelog: https://github.com/Abhi-Subedi/LocalDrop/blob/v1.1.0/CHANGELOG.md
Upgrade notes: forward-only migrations. Take a backup first (docs/BACKUP.md).