- JavaScript 63.6%
- Shell 25.7%
- HTML 10.1%
- Dockerfile 0.6%
Tooling to run Photopea fully offline and self-hosted, rebranded to 'Photoshop' with a rebuild disclaimer: - ops/: mirror + patch + update scripts, nginx + docker-compose, font extractor - offline hardening: strip telemetry/ads, self-host lock unlock, ad guard, CDN guard - rebrand Photopea->Photoshop + disclaimer, plugin auto-registrar - site/plugins/removebg: self-hosted Remove BG plugin (wired to rembg via /ai proxy) - verify/: network-blocked Playwright harnesses - docs/: reverse-engineering + architecture notes The proprietary Photopea payload (site/ assets) is NOT included; build it with ops/update.sh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| ops | ||
| site/plugins/removebg | ||
| verify | ||
| .gitignore | ||
| docker-compose.baked.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
Photopea — fully offline, self-hosted
A complete local mirror of Photopea served by nginx in Docker. Runs with zero network access — verified by an adversarial test that blocks every non-localhost request while driving a full editing workflow. Built for working offline (on a plane, off-grid, wherever) with no dependency on the internet.
http://127.0.0.1:8887
Quick start
cd ~/photopea-offline
docker compose up -d # nginx serving ./site on 127.0.0.1:8887
open http://127.0.0.1:8887 # or http://127.0.0.1:8887/# to skip the splash
Stop / start:
docker compose down
docker compose up -d
What's inside
| Path | What |
|---|---|
site/ |
The whole Photopea snapshot — 6090 files, ~2.8 GB (code, resources, the full 5912-file font library, JXL/HEIF/AVIF codecs, UI fonts). |
docker-compose.yml |
nginx:alpine, bind-mounts site/, binds to 127.0.0.1:8887 only. |
ops/nginx.conf |
MIME/gzip/cache config + a local /papi/ stub so telemetry never leaves the machine. |
ops/mirror.sh |
Downloads every asset in ops/manifest-all.txt (resumable). |
ops/patch.sh |
Offline-hardening source patches (see below). |
ops/update.sh |
Refresh the snapshot from the live site when online. |
ops/extract-fonts.js |
Decodes the font DB out of DBS*.js to regenerate the font list. |
verify/matrix.js, verify/matrix2.js |
The adversarial offline tests. |
Dockerfile + docker-compose.baked.yml |
Optional: bake the snapshot into a portable image. |
Verified working offline (network fully blocked)
Two Playwright harnesses drive real editing while aborting and logging every
request that isn't 127.0.0.1. Latest run: all checks green, 0 external
requests, 0 missing assets, 0 console errors.
- App loads; new document; text with the on-demand font library (fonts stream
from the local mirror, both the
fs/andgf/pools); HarfBuzz/FriBidi text shaping (hb.wasm). - Filters (Gaussian/Motion blur, Unsharp, Add Noise); adjustments (Levels, Desaturate, rotate, resize).
- Open & save PSD (round-trip verified — save then reopen). Open PNG.
- Export PSD, PNG, JPG, WEBP, GIF, BMP, TIFF, SVG, PDF (all valid signatures).
- Resource packs (
basic.zip,fa.zipcustom shapes) load from the mirror.
Re-run any time (container must be up):
cd verify && node matrix.js # core workflow (14 checks)
node matrix2.js # codecs + filters + adjustments (13 checks)
The harness temporarily writes site/_verify_wrapper.html and removes it on exit.
What does NOT work offline (by design — these need Photopea's servers)
None of these are fixable by a mirror; they call online-only backends:
- AI features — Remove Background, Generative Fill/Replace, upscaling.
- Account / premium — login, buying premium, license keys (stubbed to no-ops).
- Cloud storage — Google Drive, Dropbox, OneDrive, PeaDrive.
- Open image from URL and the Templates gallery (fetch from remote servers).
- Codec files for HEIC/AVIF/JXL are mirrored and served (they load same-origin, so decoding a local HEIC/AVIF should work offline), but that path is exercised through the Open/Export dialog UI, not the scripting API, so it wasn't asserted end-to-end by the automated harness.
Everything else — open/edit/save all common raster & vector formats, layers, masks, smart objects, adjustment layers, channels, paths, brushes, text, filters, Camera Raw (RAW decode is built in) — works fully offline.
Offline hardening applied (ops/patch.sh)
Surgical string patches to pp.js + index.html (validated with node --check):
- Zero egress.
papi/*telemetry & account calls are rewritten to resolve at the local nginx (which answers{}), so nothing leaves the machine even when the laptop is online. - Ads removed. All four ad-script injectors neutralized (Google Publisher Tag
via doubleclick/googletagservices, plus
nuload.net/photopea/ad.js), and the "Are you blocking ads?" nag dialog removed from the notification rotation. - CDN-escape guard.
aw.Sk()is forced to resolve fonts/icons relative, so the app can never reach out tovecpea.comeven if served from aphotopea.comhostname. - Google Fonts (Open Sans UI font) downloaded to
site/style/gfonts/and the CSS@font-faceURLs rewritten local.
Updating the snapshot (do this online)
Photopea version-stamps its core files (pp<N>.js, DBS<N>.js, …) every release.
To pull a fresh copy:
ops/update.sh
It re-derives the current core filenames from index.html, regenerates the font
list from the new DBS, downloads new/changed files (and prunes fonts dropped from
the DB), re-localizes the UI font CSS, re-applies the patches, and restarts the
container. Then re-run verify/matrix.js to confirm.
Portable image (carry it to another machine)
docker compose -f docker-compose.baked.yml build # bakes site/ into the image
docker save photopea-offline:latest | zstd > photopea-offline.tar.zst
# on the other machine:
zstd -d photopea-offline.tar.zst -o - | docker load
docker run -d -p 127.0.0.1:8887:80 photopea-offline:latest
A note on licensing
Photopea is proprietary freeware (author: Ivan Kutskir). He explicitly states it
"runs locally already… no data is sent" and is usable offline after caching, but
objects to public re-hosting / competing / ad-supported mirrors (he has DMCA'd
those). This snapshot is for private, single-user, ad-free, offline use on your
own machine. Keep it that way: don't expose it publicly, don't add ads, don't
present it as a service. If you deploy Photopea for an organization/many users, he
sells a proper self-hosted license (photopea.com/api/accounts#self-hosted).