Lolly Beta
Launch App

Verify It Yourself

signed by Lollyverify-yourself.htmlHTMLAI generatedgenerated by GeminiCheck it yourselfGet the signed filepixels, not shapes10 KB

These procedures check standalone Lolly's local processing and public services. Optional lolly.work adds organisation sign-in, shared content, configured telemetry and audit records; check those separately against Server Surface and the organisation privacy section. Each procedure below identifies the workflow being checked.

If any check on this page doesn't produce the result shown, that's either a bug or a broken promise. Report it either way, and we'll treat it with the severity a broken promise deserves.

See it work, in ten seconds

Before the procedures, the payoff. Open /verify and drop a file on it - no upload, no account, no wait for a server. Here it is checking the generated Queensland storm from our AI stance page: a Gemini image Lolly opened, resized and exported. Every badge below was computed on the device, from the file's own bytes.

Verify on a phone-width screen - the storm image, a green Made with Lolly verdict and the credential-intact and bytes-unchanged badges stacked beneath itsigned by Lollyvector SVGCheck it yourselfGet the signed file79 paths~6.4k nodes48 groups1 image149 KBVerify on a phone-width screen - the storm image, a green Made with Lolly verdict and the credential-intact and bytes-unchanged badges stacked beneath itsigned by Lollyvector SVGCheck it yourselfGet the signed file79 paths~6.4k nodes48 groups1 image149 KB

The verdict is not one badge but a small pile of them, each an independent fact:

And the whole history travels with the file. Nine steps survive here - five Google recorded as it generated and watermarked the image, then four Lolly recorded as it opened, marked and converted the copy on this page - read straight back out of the bytes, on your device, and rendered as a timeline. This is the same image, verified the same way, as the C2PA timeline on the AI stance page.

The change history Verify reads back out of the storm image - five steps recorded by Google, then four by Lolly, ending in the WebP on this pagesigned by Lollyvector SVGCheck it yourselfGet the signed file255 paths~14k nodes162 groups379 KBThe change history Verify reads back out of the storm image - five steps recorded by Google, then four by Lolly, ending in the WebP on this pagesigned by Lollyvector SVGCheck it yourselfGet the signed file255 paths~14k nodes162 groups379 KB

None of that is the trust claim, though - it is the demo. The rest of this page is the trust claim: every badge above is reproducible, and here is how you reproduce the guarantees underneath them.

In your browser, no tools required

1. Watch the network. Open lolly.tools, open your browser's DevTools (F12), switch to the Network tab and use a tool - type a URL into QR Code, change colours, export a PNG. Every request stays on lolly.tools: the app shell, the tool's own files, catalogue assets. No analytics host, no CDN beacon, no font service, no "error reporting" endpoint. What you type into a tool appears in no request at all - rendering is local.

Other standalone features are visible in the same Network tab when used: adding a Google Font in the brand editor fetches that one family from Google, after a consent dialog that tells you exactly that, once, before the first fetch; clicking an ICC press-profile preset fetches that profile from the ICC's public registry at color.org; playing the optional built-in radio streams from the station; entering a location in Meeting Planner looks that place up at open-meteo's geocoding service for its coordinates and time zone, once per city (answers are saved on your device), and the input carries that disclosure right where you type; and URL Screenshot necessarily loads the URL you typed - that's its job, and you watch it happen. A tool that declares a network capability may fetch only from the hosts its manifest allowlists, and that mechanism is fail-closed; no tool currently shipped declares one, so the browser-enforced Content-Security-Policy is the boundary that actually holds the list above to its hosts. The privacy policy keeps the canonical table of all of these; it also describes managed-instance traffic, including policy refreshes and enabled reporting during use.

2. Pull the plug. Load the app and open a tool or two, then go offline - airplane mode, or DevTools → Network → Offline. Reload. The gallery and every tool you've opened keep working, including rendering and export in the formats you've used - a tool's files and a format's encoder are cached the first time you use them, so exercise a tool once online before testing it offline. This is the strongest single check on this page: software that phones home doesn't survive its cord being cut.

3. Count the cookies. DevTools → Application (Firefox: Storage) → Cookies → https://lolly.tools. The list is empty - the app sets no cookies. Or paste document.cookie into the console: you get "". (The optional CA enrolment flow sets a short-lived HttpOnly cookie, lolly_ca_state, scoped to /api/ca; document.cookie cannot show an HttpOnly cookie. A lolly.work instance also uses its own authentication cookies. Inspect them in the Application panel and compare with Privacy.)

4. Read your own storage. Same Application panel: everything Lolly keeps is inspectable in front of you - a couple of dozen plain localStorage keys (theme, language, sidebar width, sound and view settings, plus a cached copy of the public tool-catalogue index), and your own documents in IndexedDB. Every value is a readable string or JSON - nothing is obfuscated, nothing is encoded to discourage reading it. Clearing site data removes these local records; it does not remove a Sync backup or records held by a lolly.work instance; Settings → Storage → Clear all my data does the same from inside the app, and you can watch the stores and caches empty in this panel.

5. Check the disclosure contact exists. /.well-known/security.txt answers with an RFC 9116 contact block, not an HTML page.

From a terminal

6. The render endpoint answers with public data only. The one server feature that puts user-typed inputs into a URL - hot-link renders - is live here, and the privacy policy says what that means for the inputs you put in a link:

curl -s -o /dev/null -w '%{http_code}\n' 'https://lolly.tools/tool/qr-code.svg?url=test'
# 200

The switch is per-deployment (LOLLY_DISABLE_RENDER_GET=1): on an instance that sets it, the same probe returns 404 - that difference is the flag working, not an inconsistency.

7. Check the services your deployment enables. Server Surface identifies optional services and links their detailed references. For lolly.work, include its authenticated APIs, collaboration connection, storage and configured providers. A public lolly.tools probe does not test a private organisation instance.

In the source

Everything above could still be theatre if the deployed code differed from the public code. So check the code - the deployment builds from the public repository:

git clone https://github.com/lolly-tools/lolly.git
cd lolly

The following source scan checks third-party tracking SDKs. It does not establish the absence of first-party usage reporting: lolly.work supports organisation telemetry and a separate audit log, with their own policy and tests.

8. No tracker, no analytics SDK, anywhere. Search the code that ships - the engine, every shell (including the browser extension, the Tauri bridge overrides and the service worker), the server functions and the tool packs - for the usual suspects:

grep -rn --include='*.ts' --include='*.js' --include='*.mjs' --include='*.html' \
  -e googletagmanager -e google-analytics -e posthog -e mixpanel -e hotjar \
  -e sentry.io -e segment.com -e '@vercel/analytics' \
  engine/src shells/*/src shells/chrome-extension shells/*/bridge-overrides \
  shells/web/public/sw.js services packages api community brands/*/tools
# (no output)

9. No third-party DNS resolver. Verify's SEAL check never routes lookups through a DNS-over-HTTPS provider - the web app simply has no resolver:

grep -rn --include='*.ts' --include='*.js' --include='*.mjs' \
  -e cloudflare-dns.com -e dns.google -e /dns-query \
  engine/src shells/*/src shells/chrome-extension shells/*/bridge-overrides \
  shells/web/public/sw.js services packages api community brands/*/tools
# (no output: outside this page and its built copies, the only place those
#  hostnames appear in the repo is tests/no-trackers.test.ts, the test that
#  bans them)

10. The certificate service retains nothing. The identity CA has no issuance log - not your email, not a timestamp, not a webhook. The absence is greppable:

grep -rn logIssuance services/ca api/ca
# (no output)

Enforced by tests, not promises

The three source checks above aren't a one-time audit - they're pinned in the test suite, so they can't quietly rot. tests/no-trackers.test.ts fails the build if:

Run them yourself in the clone (Node 22.18+; no pnpm install needed for this file):

node --test tests/no-trackers.test.ts
# ✔ no analytics or tracking SDK appears anywhere in shipped source
# ✔ no third-party DNS-over-HTTPS resolver is contacted
# ✔ the certificate service logs no personal data
# ✔ privacy policy states a controller, a legal basis and a right to complain

The full suite (pnpm install && pnpm test) runs several thousand more, including the adversarial cryptography tests described in Security & Verification.

What you can't verify from outside - said plainly

A page like this earns trust by naming its own limits:

If a check fails

A discrepancy between this page and observed behaviour is a security report, and we'd genuinely rather hear it than not: fitzy+security@suse.com, the Report a vulnerability button on any lolly-tools repository or the contact in /.well-known/security.txt. Coordinated disclosure and reporter credit are the standing policy - SECURITY.md has the details.