Archnemix

Image Protection · Open Source · 0BSD

For photographers, galleries & shops who sell pictures

Publish the picture.
Keep the picture.

In server mode, Archnemix serves authorized images in scrambled pieces behind one-time tokens, reassembled inside a guarded viewer. Keep masters outside every declared public root. The server bounds each derivative by its configured entitlement and width caps; set maxWidthFraction below 1 if native-resolution pixels must never be delivered. Extraction tools get permuted tiles and decoys; visitors get the photograph.

Your visitor’s view

Reassembled, per-viewer, inside a canvas — never as an <img> with an address. Drag to orbit it, hover a tile to trace it, click to scatter.

Grid Hover a tile to trace where it travels. 64 tiles · seeded permutation · drag to orbit · hover a tile · space to scatter

No live demo on this static website: the hero is an explanatory illustration only. There is no Archx server here to authorize a viewer, mint and spend a token, keep a master private, or build a Sharp derivative — that server is the main protection boundary. The real end-to-end demo is run locally from the GitHub repository. The grid choices above (8–32) are the same range the product permits for new payloads: coarser grids are refused at creation because tile-edge jigsaw solvers reassemble them without any key.

npm install @archnemix/image-protection Node ≥ 20.9 · pure-JS client · Sharp on the server · free for any use
01

The ordinary ways pictures walk away

Right place, wrong hands

A normal <img> tag is a public URL. View-source, DevTools, or one bulk scraper later, your full-resolution master is on someone else’s storefront — metadata, quality and all.

Watermarks only go so far

Visible marks can be cropped or obscured; metadata is often stripped by re-upload. Neither controls who receives the pixels or stops an authorized viewer from capturing them.

“Disable right-click” is theatre

It annoys your customers and stops nobody. Real protection has to change what the server delivers: keep the master private, authorize each view, and apply quotas plus a width policy. Scraping can be limited, not eliminated.

02

How Archx works

  1. i.

    Masters stay off the web

    Keep originals outside every public root and declare those roots with publicDir/publicDirs. The middleware checks declared overlaps at startup; undeclared host mounts remain your responsibility. The middleware is the only intended reader.

  2. ii.

    Every view is authorized, scrambled & replay-limited

    Your authorize callback decides who may see what, and how large. The server applies the signed entitlement and configured width caps, permutes the tiles under a 128-bit-seeded plan, optionally seals them with AES-256-GCM, and issues a short-lived token with one-time claim enforcement while its spend record is retained. The default maxWidthFraction is 1, so set it below 1 when native-width pixels must be withheld.

  3. iii.

    The viewer reassembles — and poisons the exits

    Canvas/WebGL rendering with no ordinary image URL to copy. Read-back gets a decoy; capture signals may trigger a black or degraded frame. Deliveries can carry an invisible StegoMark fingerprint tied to an issuance record; in the project’s 99-case fixed image-transform bench it recovered in 85 cases. The matrix has no screenshot/camera capture, and is not a universal recovery rate.

03

The layers, in plain terms

LayerWhat it doesWhat it costs
Private mastersKeep originals outside public roots; startup rejects overlaps with roots declared to Archx.Nothing at runtime
Entitled derivativesOnly the authorized, width-capped derivative leaves; with the default fraction of 1, native-width pixels may be allowed. Set maxWidthFraction below 1 for preview-only delivery.One Sharp transform, cached
Tile scramble + optional AES-256-GCMRaw network capture gets scrambled bytes; reconstruction is possible with enough work. AES-GCM optionally protects the payload from parties without its key.Fused pad + scramble measured at ≈2 ms at 960 px; Sharp decode/resize/encode is separate
One-time signed tokensHMAC tokens are spent on first use; sender constraints and authorization reduce replay and hot-linking, while new mints remain subject to host policy.Microseconds
Guarded viewerNo src to steal; poisoned read-back; screenshot-degrading temporal interlace at 60 Hz.GPU-cheap canvas work
StegoMark fingerprintInvisible per-recipient mark with BCH error correction; identified later by rescan, no original needed.≈9 ms at 960px, opt-in
Abuse budgetsRate limits and distinct-image quotas bound collection and resource use; public anonymous minting can still be harvested within active limits.Counters, not compute
04

Measured, not promised

Every number below comes from the benchmark and attack suites shipped in the repository. Run them yourself: npm run bench:stego, npm run redteam.

85/99robust StegoMark recoveries in the fixed transform matrix (20 s per-call budget; 14 timeouts; no screenshot or camera captures)
0/369wrong-key positives in this finite sweep (one-sided 95% upper bound ≈0.81%; not proof of zero risk)
53/53scored defenses held in red-team v1 (55 cases total, 2 informational); red-team v2 held 32/32
168+129Node tests (including seeded fuzz cases) and real-Chromium browser checks, plus a packaged-artifact smoke test
05

Use it in an afternoon

On the server

import { createArchxMiddleware }
  from '@archnemix/image-protection/server';

app.use(createArchxMiddleware({
  imagesDir: '/private/masters',   // outside public/
  publicDirs: ['/var/www/public'], // declare every static root for overlap checks
  maxWidthFraction: 0.6,           // omit native-width pixels for preview-only delivery
  secret: process.env.ARCHX_SECRET,
  authorize: async (req, image) =>
    req.user ? { maxWidth: 1600 } : false,
}));

In the page

import Archx from '@archnemix/image-protection';

Archx.load.image('#hero-photo', { id: 'aurora-valley' }, {
  tokenEndpoint: '/archx/token/',
  profile: 'elite',     // lite · performance · elite — or custom flags
  temporalInterlace: { refreshHz: 60 },
});

Three profiles set the defaults — lite spends the least CPU and memory, performance balances protection and cost, elite turns every hardening layer on — and any individual setting can still override them.

Prefer static hosting? npx archx-build creates scrambled derivatives and a manifest for CDN delivery. Its keys are browser-delivered, so a public manifest is not access control or DRM; keep private catalogs behind your own authorization/CDN layer. The dynamic server middleware works with Express, Fastify, or plain node:http.

Documentation, in reading order

The complete reference — every option, CLI flag, and the deployment runbook — lives in the project README and operations guide on GitHub.

06

Free as in “it’s yours”

Archnemix Image Protection is released under the BSD Zero Clause License (0BSD). Use it, copy it, modify it, ship it in commercial products — for any purpose, with or without fee. No attribution is required and no permission need be asked.

If it saves your work from being lifted, a credit line or a link back is warmly appreciated — and entirely optional. That is the deal, in full; the license text fits in a paragraph in the repository.

Get it on GitHub ↗ npm ↗