# Piggyback web application design

App resource parts can be held in universal appended binary carriers, PNG `amWd` ancillary chunks, MP4 `uuid` boxes, PNG RGB low bits, audible WAV tones, or sparse lossless PNG frame pixels. The first three store absolute carrier-file byte ranges; the latter three store decoded-payload offsets and require a full media fetch. The browser verifies each stored part and complete decoded resource. New `frame-luma2` parts use texture-aware, mathematically scattered one-level red changes with three bit copies; legacy `frame-luma1` readers remain for older visible-tile releases. Container-native packing retains an AMW footer for offline discovery; image optimizers and video transcoders can remove embedded data. The MP4 box profile does not extract app bytes from picture frames. Amoosed.com carries a second playable film as an app resource inside the ordinary MP4 UUID box and embeds a small `AWV1` fingerprint in three picture frames for a browser Canvas cross-check. This is not a general video-frame website carrier. The demo app's resource-first carrier table reads the verified favicon index to show each app file and its chunk locations; it does not independently recheck every chunk. See [FORMAT.md](FORMAT.md).

The site-owned runtime reserves the path `/amoosed.com/` **on the existing site's origin**. For example, `https://customer.example/amoosed.com/app/index.html` is a virtual app URL; it does not navigate to the amoosed.com domain. The host must not already use this path for another purpose. App pages under `/amoosed.com/app/` remain carrier-backed virtual resources. The small boot and worker routes are still a separate host integration requirement.

An ordinary page can use a small PNG in `<link rel="icon" type="image/png" href="/moose-icon.png">`; this does not change discovery. Amoosed.com does so for its visible tab icon, while `/favicon.ico` remains a valid multi-size ICO with the appended AMW index for compatible readers and browsers that request the default favicon path directly. A publisher using an external private index may leave the ordinary favicon unmodified.

## Distribution contract

The ordinary website and its normal URLs remain intact. `/favicon.ico` is the AMW index carrier. The alternative app's built HTML, CSS, JavaScript, images, fonts, JSON, and WASM bytes are divided among **existing** binary files selected by the publisher, such as images or videos. `bundle` writes modified copies at the same relative paths. Its overlay contains no new payload paths and no original site `index.html` or unbundled app files. The deployer merges these replacements into the existing site build.

Version 0.6 renamed the prototype runtime path from `/amw-app/` to `/amoosed.com/`. Rebuild both the carrier index and the site runtime together when upgrading; an older index's launch and resource paths will not match this runtime. The AMW payload header remains v1. New index carriers use footer v2 to anchor the progressive intro; readers still accept footer v1 and delay its intro until complete verification.

Version 0.7 adds optional app identity metadata, a carrier-backed virtual favicon, and a site boot page that waits for Start after showing the verified intro. Update the extension and the site runtime together. The AMW header and carrier footers do not change.

Version 0.8 makes a plain boot visit an automatic loading screen with verified chunk progress, bounded parallel downloads, and a worker-memory handoff. The authored bootstrap title, text, and theme customize its appearance; the site boot ignores manual `launch` blocks, while the limited extension document viewer may still use them. The boot page fades out before navigation; the demo app supplies a matching entrance animation. The boot intro appears over 900 ms; after preparation it shows a visible two-second countdown and pulsing progress bar even on a warm cache, then its exit lasts 2.4 seconds and navigation waits another 300 ms after animation completion, and the demo app entrance takes one second. The extension-owned side panel sections appear over 900 ms, and incoming chunk markers change over 400 ms; reduced-motion preferences disable these transitions. The side panel uses a grayscale moose toggle on ordinary site tabs to launch the alternative in a new tab; it turns color on boot, app, and viewer tabs, where clicking it returns to the original site. During a plain boot load it pulses slowly and keeps transient verification text hidden, while permission prompts and errors remain visible. It visualizes arriving parts and receives detailed loading errors through a bounded, isolated bridge. Update the site runtime and extension together. The wire format is unchanged.

The input site build is read only. Carriers are explicitly listed; there is no automatic modification of every media file on an existing site. Any existing opaque binary file can be selected, including formats or extensionless paths the CLI does not know in advance. Obvious page, script, stylesheet, and text files are rejected as carrier candidates. An extension does **not** guarantee that every decoder, media player, image optimizer, signature, or CDN preserves trailing bytes. Test each carrier as normal media after deployment. Thumbnail generation, CDN recompression, and image proxies may strip the data; use original static URLs that preserve exact bytes.

Reduced-motion mode still displays the ready countdown for two seconds after verification. It omits the exit animation and glow, then opens the app. This keeps the launch stage visible when Chrome or the operating system requests less motion.

The moose in the side panel cancels a plain boot load back to the original site. From an open app it starts a site-owned shutdown screen at `/amoosed.com/boot.html?shutdown=1`, with a draining progress bar and closing orbit, then returns the tab to the origin root after about 2.7 seconds. The same moose cancels shutdown during that window and resumes the exact Amoosed URL. Its `resume` value is kept in the URL fragment and validated as a same-origin Amoosed route by the extension; the shutdown page ignores it and always returns to `/`. Reduced-motion mode keeps the closing screen visible for two seconds without animation.

## Index and resource map

App resources are opaque bytes: the carrier format does not restrict file extensions. The bundle command recognizes common browser asset MIME types and `application/zip`; unknown extensions use `application/octet-stream`. To mark any file for download, list its app-build-relative path in the bundle config, for example `"downloads":["archive.zip","reports/data.custom"]`. A download resource gets `"delivery":"download"` in the manifest and is returned with `Content-Disposition: attachment` and its validated filename. App HTML should use `<a href="archive.zip" download>Download</a>` and include `<script type="module" src="/amoosed.com/download.js"></script>`. This small site-owned helper fetches through the verifying service worker, checks the attachment response, and saves the verified bytes. Chrome may bypass service workers for a direct anchor download, causing the static host's boot fallback HTML to be saved as the requested filename; do not rely on a bare link. The boot worker does not prefetch download resources; it verifies their chunks only when requested. Each resource still has the current 1 GiB length ceiling and is assembled in memory before being returned, so this prototype is intended for bounded files rather than multi-gigabyte streaming downloads. Protected downloads require the same content key as other protected resources.

The AMW v1 document section has an optional `webApp` object. The 4 KiB header and 128-byte index-footer size remain unchanged. The index points to virtual paths and carrier byte ranges:

Paths such as `/amoosed.com/app/index.html` are **virtual browser URLs**, not files to place in the public build. The site-owned worker answers them by extracting verified bytes from the selected carriers. The demo build now checks that no mapped app resource exists as a separate deployed file and that the release contains no source maps. The ordinary site's own `/index.html` remains its regular page.

```json
{
  "launch": "/amoosed.com/boot.html",
  "entry": "/amoosed.com/app/index.html",
  "identity": { "title": "Alternative edition", "icon": "/amoosed.com/app/favicon.ico" },
  "resources": [
    {
      "path": "/amoosed.com/app/index.html",
      "mime": "text/html; charset=utf-8",
      "length": 1234,
      "sha256": "64 lowercase hex digits",
      "parts": [
        { "url": "/media/cat.png", "offset": 8742, "length": 1234, "sha256": "64 lowercase hex digits" }
      ]
    }
  ]
}
```

Resources are unique and sorted. Part URLs must be same-origin absolute paths and cannot point into `/amoosed.com/` or `/favicon.ico`. Each plaintext part is up to 4 MiB; encryption adds a 16-byte authentication tag. A part's hash and length, then its assembled resource hash and length, are checked before a browser response is exposed. SHA-256 ties bytes to the downloaded index; it does not provide a publisher signature. HTTPS and control of the host remain essential.

The compiler now puts a public, deterministic 256-bit website encoding key in the favicon index as `webApp.encoding`. It applies AES-CTR to every app part after any members-only AES-GCM encryption, so plain HTML, scripts, images, and downloads are not stored as directly searchable strings in carriers. The original ordinary-site media prefix is unchanged. A detached carrier without the favicon index lacks the key and resource map, but this is obfuscation, not secrecy: anyone who can fetch the index can decode public resources. Any app resource change rotates the derived key and re-encodes all parts; new-index/old-carrier cache mixing fails part hashes. A complete cached old index and matching old carriers can still serve an old snapshot. Existing manifests without `encoding` remain readable as raw parts. All extension and site runtime copies must be rebuilt together for the new profile. See [FORMAT.md](FORMAT.md).

For a hosted index, optional `encodingSalt` in the bundle config adds 32 public bytes to the key derivation; `rotate-project ... site` sets a new value without changing app files. This changes encoded bytes but grants no access restriction because the result is published in the index. For an external index, `site` rotates the separate private index key. Member key rotation is independent. See [ROTATION.md](ROTATION.md).

When `identity` is present, its title is bounded metadata for the app and virtual favicon; the extension does not display it in its side panel. Its icon path is fixed to `/amoosed.com/app/favicon.ico`, and that resource must be public with MIME type `image/x-icon`. The virtual favicon is assembled from carrier ranges like any other app resource. Its visible ICO prefix is followed by a small, compiled AMW document with the same title and a universal 128-byte footer; offline verification checks the embedded title against the root index. The bundle command automatically creates it from the existing site's icon, or wraps an app-provided `favicon.ico`. App HTML should link to the virtual icon URL. It adds no public deploy path. Older manifests without `identity` remain readable. This additive v1 manifest field requires an updated extension and site runtime together.

For an encrypted edition, `webApp.encryption` is `{ "algorithm": "AES-256-GCM", "keyId": "edition-1" }`. A resource may declare `"access":"public"` or `"access":"members"`; when the encryption object exists, omitted `access` retains the earlier all-protected behavior. Member parts gain `nonce` (24 lowercase hex characters) and `plainLength`; public parts have neither. A part's `length` and `sha256` refer to stored bytes after site-wide encoding; its resource's `length` and `sha256` refer to complete plaintext. Member encryption adds a 16-byte authentication tag before the outer encoding. Chunk AAD binds path, MIME type, part index, and plaintext length. The browser checks stored bytes, decodes with the public favicon key, then authenticates member parts and verifies reconstructed plaintext. The index document stays public. The mixed demo makes its app entry public and protects `members/`; details and limitations are in [ENTITLEMENTS.md](ENTITLEMENTS.md).

Each data carrier is `original bytes || appended data || 128-byte AMW1DATA footer`. The footer stores original and appended lengths plus SHA-256 of each. Offsets in the resource map are absolute within the packed carrier. The offline verifier checks complete carrier hashes and resource hashes. The browser fetches only referenced byte ranges and checks part/resource hashes, so it need not download unrelated media prefixes. A host ignoring Range is supported only for carriers up to 16 MiB; larger videos need a range-capable endpoint. Browser readers stop oversized responses while streaming, even when the host omits or misstates `Content-Length`.

## Running the app

The **demo** ships `/amoosed.com/boot.html`, a module service worker, and small loader modules. The amoosed.com side panel launches that site-owned route without rendering site-authored content inside the panel. A plain boot visit progressively shows the authored title, text, and theme after its prefix verifies. A generic animated background and progress bar remain visible while the complete index verifies, the worker activates, and each public app resource is reconstructed and checked. The worker fetches up to four parts per resource and two resources at once, then assembles parts in manifest order and checks the final resource digest. A protected entry is also checked if a saved key exists; without a valid key, the boot page asks for one. The worker reports verified chunk progress and keeps at most 32 MiB of prepared public resources in memory for the immediate app load. A worker restart or a larger app can cause a fresh verified fetch; decrypted member resources are never retained in this cache. The boot page fades out and navigates automatically to the entry when preparation finishes. The demo app fades in through its own CSS; customer apps can add their own entrance effects. Motion is disabled for users who request reduced motion. A cold direct app URL, member return URL, key-share link, or panel key operation follows its specialized path without stopping at the intro. The worker intercepts only `/amoosed.com/app/*`, reads the verified index, reconstructs a requested resource from existing media carriers, and returns the declared MIME type. Public resources load without a key. Member resources need a verified key; a direct member-page visit redirects through the boot route with a validated return path. Chrome then uses its native HTML, CSS, JavaScript, image, and WASM engines at the site's ordinary origin. SPA navigation under the app scope falls back to its entry HTML. The worker leaves the normal site outside its scope.

During the boot visit, the side panel builds its own carrier visualization from the verified index. A small content script observes the boot page's progress attribute and relays bounded snapshots; it never reads app bytes or receives a content key. The panel validates the tab, origin, route, resource paths, and part indices, then lights the arriving parts. A late-opened panel sees the current count and up to the last 512 part identities. Because the hosting page can change its DOM, these live signals are informational. **Advanced → Check chunks** independently fetches and hashes carrier parts when diagnostic certainty is needed. A loader error appears in detail in the panel, including a carrier URL when a part failed; the main loading page shows only a concise failure message.

The virtual resource route accepts GET and HEAD, verifies the same bytes for either, and omits the body for HEAD. Other methods return 405. Resource, missing-resource, key-required, redirect, and verification-error responses all use `Cache-Control: no-store`; the worker never treats a failed check as a reusable cached result.
An initial direct visit to `/amoosed.com/app`, `/amoosed.com/app/`, or a deeper app path must return the boot page when no worker controls the browser yet. The boot script registers the worker and opens the verified entry or reloads the controlled deep link. The generated static build and any custom hosting adapter must provide that cold-start fallback.

For Cloudflare Pages, the demo build includes exact `/amoosed.com/app` and `/amoosed.com/app/` rules before the `/amoosed.com/app/*` rule in `_redirects`. These serve the boot page at a cold app URL. The worker also accepts the extensionless `/amoosed.com/boot` path that Pages may use when canonicalizing an HTML file URL. Test these routes after deployment, especially if Pages Functions or other redirects are present; Pages Functions do not use `_redirects` rules. See Cloudflare's [Pages redirects](https://developers.cloudflare.com/pages/configuration/redirects/) and [page serving](https://developers.cloudflare.com/pages/configuration/serving-pages/) documentation.

Those runtime paths are an **integration requirement**, not part of the zero-new-files carrier overlay. An existing site can provide them through its own route adapter, rewrite rules, or static files, regardless of its application framework. A browser extension cannot silently install a service worker on a customer's HTTPS origin, and ordinary Chrome cannot give an opaque extension page the site's origin. If the site cannot provide these routes, the piggyback data is still packable and verifiable, but the full site-origin app cannot launch. The limited declarative intro and full document viewer continue to work in the extension.

For a custom host, serve the boot HTML as `text/html` at `/amoosed.com/boot.html` and cold `/amoosed.com/app/*` visits. Serve `/amoosed.com/sw.js` as JavaScript with a scope covering `/amoosed.com/app/`; serve its same-origin loader modules with correct MIME types. Keep selected carrier URLs and their byte ranges unchanged by proxies and optimizers. Verify those paths, headers, and hashes before offering the alternative app. The published compiler currently generates the static Pages runtime, but does not supply a ready-made route adapter for every existing server.

Web features run under the host's actual policies: CSP, CORS, cookies, service-worker permission, MIME rules, and HTTPS still apply. App JavaScript has ordinary site-origin powers and should be reviewed by the site owner. It never gains Chrome extension APIs. The AMW extension itself keeps downloaded code out of privileged pages. Chrome controls side-panel width and requires a user gesture to open it.
For encrypted resources, the extension side panel or boot route accepts an owner-shared 32-byte content key represented as 64 hexadecimal characters. The boot route can also read the key from a private `#amw-key=` URL fragment and removes that fragment immediately; it is never included in the HTTP request. The worker authenticates the smallest protected chunk before storing a nonextractable key in same-origin IndexedDB; it verifies each complete resource when that resource is requested. This keeps a large protected video from being downloaded merely to test a key. This prototype uses shared keys rather than a hosted entitlement or payment system. The side panel is amoosed.com-owned: it shows access controls and an Advanced entry in its bottom bar, never site-authored blocks, styles, or links. Advanced maps carrier chunks to resources and checks each referenced stored-byte chunk hash without needing a content key; it does not check original media prefixes.

Member key sharing also supports a local `.amwmember` file and a QR or HTTPS boot link made with `amw member-link`. The `AMM1` token carries the edition key ID and raw content key; the boot page removes `#amw-member=` immediately, checks the ID against the verified app index, and asks the same worker to authenticate the key. The `return` query must name a protected app resource. The starter compiler writes the sharing file outside `public/`; neither it nor the QR belongs in the hosted release. See [ENTITLEMENTS.md](ENTITLEMENTS.md).

For `indexDelivery: external`, `/favicon.ico` is the site's ordinary favicon. The boot page accepts either a separately shared packed ICO or a compact `.amwkey` text file. It validates that credential, fetches the encrypted index from the relative carrier URL listed in it, and passes it to the same-origin worker. The worker independently authenticates the index and a public app-icon resource before saving the credential in same-origin IndexedDB for later visits. The extension's information panel offers a route to this import page on sites without a hosted index. On a running app tab, it can independently check the imported credential and encrypted carrier range before showing its mode controls. Clearing that site's browser data removes the saved index. Website code at the same origin can access its origin's stored credential; the extension does not keep a separate copy.

The owner can deliver that ICO as an email attachment with ordinary project correspondence, such as a design brief or question. It can serve as a manually issued paid-entry credential because the website needs only the encrypted carrier build. The recipient may forward or copy the file, so the deployment must not claim customer-specific entitlement, expiry, or revocation. Optional member encryption adds a separate shared key for selected resources; see [ENTITLEMENTS.md](ENTITLEMENTS.md).

`amw key-link` can turn either external key file into a compact QR unlock link for event posters or business cards. The boot route handles its `#amw-index=` fragment and sends only the decoded token to its same-origin worker. The worker applies the same carrier and identity checks as file import before saving the token in origin-scoped IndexedDB. A QR scan therefore needs only an ordinary HTTPS navigation, not a custom browser protocol. On an active app tab, the extension can independently resolve the saved token and show the verified mode state. This does not add extension storage or give the extension automatic control of Chrome's side panel.

## Publishing and updates

1. Build the ordinary site and alternative app separately. Avoid source maps in the app build.
2. List exact existing carrier paths in the bundle config. The favicon and listed files must be served byte-for-byte at the same paths. Keep carrier URLs stable during a release.
3. Run `bundle` into a separate overlay directory. Inspect and verify it; merge only the replacement files into the site's deploy artifact. Check that the normal site still serves its HTML and media correctly.
4. Provide the small `/amoosed.com/` runtime route if full app execution is desired. Cache the favicon and runtime with revalidation or `no-store`; do not let a CDN mix a new index with old carrier bytes. Content-addressed immutable caching is not used for these existing media paths.

The bundle is deterministic for identical inputs, config, and carrier order. Repacking strips a valid previous AMW suffix before writing a replacement. The build refuses symlinks and path traversal in app inputs and carrier names. The overlay itself never contains app source filenames as deployed paths; its binary bytes remain inspectable and recoverable. Hosted-index packaging is an encoding and distribution method, not source protection. The optional external-index profile encrypts its index and derives a secret data key from the separately delivered key file; a recipient can still inspect all decrypted client-side resources.

Offline verification requires the hosted favicon or a separately supplied external `.amwkey` or ICO and the referenced carriers. The `verify-bundle` and `unpack-bundle` CLI commands reject any other file or symlink in that standalone overlay. For a merged ordinary-site build, use `verify-site` with the external key file path when applicable: it checks mapped carrier bytes, rejects source maps and directly deployed copies of mapped app resources, but cannot identify arbitrary unrelated files as app source. This prevents a symlinked release artifact from making the verifier read an unrelated local file and catches accidental app-source copies in the standalone overlay.

Packing streams both original media prefixes and alternative app assets in fixed-size chunks, so selecting large existing videos or a large alternative resource does not require loading either whole file into memory during publishing. Output is assembled in a temporary sibling directory and renamed into place only after the index and carrier footers are complete. `verify-bundle` hashes reconstructed resources as chunks without retaining them, and `unpack-bundle` writes them into a temporary sibling directory before a final rename. The programmatic verifier still offers an optional in-memory resource map for callers that need it.

## Amoosed Browser proposal

A dedicated Amoosed Browser for Android, iOS, and Windows is feasible as a separate native application. It would use normal HTTPS domains and the existing site-owned `/amoosed.com/` boot and app routes, while showing only verified Amoosed content. It is **not implemented or distributed yet**. The Chrome extension remains the way to explore both ordinary and alternative views in Chrome. An ordinary mobile browser can already follow an `amw key-link` HTTPS QR to the site's boot route where the runtime is supported; this path still needs real-device compatibility tests.

The proposed first version has a domain field, an in-app QR scanner, a blank dark state for sites without a valid Amoosed index or shared credential, and an embedded system web view for the verified boot and app. It must never fall back to rendering the ordinary homepage inside its content area. A QR holds the same HTTPS URL and transferable `AMQ1` or member token already used by the website. Scanning *inside* the app avoids relying on operating-system link association; scanning with another camera app normally opens its chosen browser until verified app links are configured. For an externally indexed site, the QR is the discovery credential. A plain domain with no public index and no previously imported key cannot reveal that private app.

The native shell should preflight the public favicon index or the supplied external credential using a reusable verifier before allowing the web view to navigate to the site's boot route. It should require HTTPS except for an explicit local-development setting; bind verified metadata and keys to the exact origin, keep ordinary cross-origin navigation out of the Amoosed-only view, and never inject privileged native APIs into site-authored JavaScript. The site's own service worker continues to reconstruct and authenticate app resources under that site's origin. Reuse the existing format, range-loader, and integrity logic across CLI, extension, and app; do not create a second codec with different verification rules. Native-held keys, if added, need per-site secure storage and an explicit forget action. In the initial design, site-origin IndexedDB is the existing credential store, independent of Chrome or Safari's profile.

Platform adapters would use Android WebView, iOS `WKWebView`, and Windows WebView2, with an app-specific domain/QR interface around them. Service-worker scope, HTTPS range requests, persistent storage, downloads, and media behavior must be tested on actual devices before advertising support. The web view uses each platform's normal HTML, CSS, and JavaScript engine; AmoosedWeb does not replace it. On iOS, [Apple's App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) require the appropriate WebKit framework for web-browsing apps and sufficient app-specific functionality for App Store submission. An integrated scanner, verified discovery, key management, and downloads are useful native features, but approval is not guaranteed. Android documents [service workers in WebView](https://developer.android.com/reference/android/webkit/ServiceWorkerController.html), and Microsoft documents [WebView2 worker APIs](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/overview-features-apis); these references do not replace testing the Amoosed runtime on those hosts.

The first milestone should be a Windows or Android proof of concept using the current public demo and one external-index QR. It should show the verified app, remain blank on an unrelated domain, reject a mismatched or stale key, and keep the ordinary page invisible. Add iOS after confirming `WKWebView` service-worker and storage behavior on target devices. QR links remain bearer credentials: any copied poster or card can be shared, and a site owner or same-origin script can access imported site credentials. This browser would improve the experience and distribution; it would not create DRM, identity-based entitlement, or automatic revocation.

## Future integration

A Chromium fork could consume the same manifest directly in browser navigation without a separately hosted service worker. A signed publisher manifest, a pinned index version per session, and streaming verification for very large assets are future protocol work. WebAssembly can be one resource in a site-origin app, and it could later accelerate the decoder; it does not replace Blink or V8.
