Back to Powder Tool V600Billion
SOURCE / PINNED RELEASE

Made of little things.

Powder Tool V600Billion

Release
142767edcab8…
Author-recorded commit
6d92971effd0…
License
LICENSE
Author’s source reference
nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy

Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

source/test/artifact.test.mjs
/**
 * The built napplet (dist/), checked offline: one document with nothing to
 * fetch; exactly the pinned single-thread build inlined (the glue with the
 * known patch, the wasm); nothing that needs threads; its own no-network
 * policy and the requires meta; the manifest template pinning the file. What
 * the Hangar pins and grants is checked in nappelin.com
 * (apps/hangar/test/powder-toy.test.mjs), against the docked copy.
 *
 * Skipped until there is a build: npm run wasm && npm run build.
 */
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { existsSync, readFileSync, statSync } from 'node:fs';
import { gunzipSync } from 'node:zlib';
import test from 'node:test';

import { GLUE_PATCHES, upstream } from '../scripts/upstream.mjs';

const dist = new URL('../dist/', import.meta.url);
const built = existsSync(new URL('index.html', dist)) && existsSync(new URL('.nip5a-manifest.json', dist));
const skip = built ? false : 'no build in dist/ (npm run wasm && npm run build)';
const D_TAG = 'powder-toy';
const REQUIRES = ['identity', 'link', 'outbox', 'storage'];

const html = built ? readFileSync(new URL('index.html', dist)) : Buffer.alloc(0);
const text = html.toString('utf8');
const sha256 = createHash('sha256').update(html).digest('hex');
const manifest = built ? JSON.parse(readFileSync(new URL('.nip5a-manifest.json', dist), 'utf8')) : null;
const inlinedWasm = () => gunzipSync(Buffer.from(/id="powder-wasm" data-encoding="gzip\+base64">([A-Za-z0-9+/=]+)</.exec(text)[1], 'base64'));

test('one document under 10 MiB (napplet.soy\'s limit) with nothing to fetch', { skip }, () => {
  assert.ok(statSync(new URL('index.html', dist)).size <= 10 * 1024 * 1024);
  assert.doesNotMatch(text, /<script\b[^>]*\bsrc=/i, 'no external script');
  assert.doesNotMatch(text, /<link\b[^>]*\brel="stylesheet"/i, 'no external stylesheet');
  assert.match(text, /<meta name="napplet-requires" content="identity,link,outbox,storage">/);
  assert.match(text, /connect-src &#39;none&#39;|connect-src 'none'/);
  assert.ok(text.indexOf('Content-Security-Policy') < text.indexOf('<script'), 'the policy comes before any script');
});

test('the pinned build is inlined: the glue with exactly the known patches, the wasm gzip-compressed', { skip }, () => {
  const glue = /<script id="powder-glue">\n([\s\S]*?)\n<\/script>/.exec(text);
  assert.ok(glue, 'glue inlined');
  assert.match(glue[1], /var create_powder = /);
  let original = glue[1];
  for (const patch of GLUE_PATCHES) {
    assert.equal(original.split(patch.replace).length - 1, patch.count ?? 1, `patch applied ${patch.count ?? 1} time(s): ${patch.find}`);
    assert.equal(original.split(patch.find).length - 1, 0, `original left in: ${patch.find}`);
  }
  for (const patch of [...GLUE_PATCHES].reverse()) original = original.split(patch.replace).join(patch.find);
  assert.equal(createHash('sha256').update(original).digest('hex'), upstream.build.files['powder.js'], 'the glue, unpatched, is the pinned build');
  assert.match(text, /<script type="application\/octet-stream" id="powder-wasm" data-encoding="gzip\+base64">[A-Za-z0-9+/=]+<\/script>/);
  assert.equal(createHash('sha256').update(inlinedWasm()).digest('hex'), upstream.build.files['powder.wasm'], 'the inlined wasm is the pinned build');
  assert.match(text, new RegExp(`<meta name="powder-toy-upstream" content="${upstream.version} ${upstream.commit}">`));
});

/** napplet-conformance's static scan (boot/no-forbidden-globals), over the whole page. */
const FORBIDDEN = {
  'window.nostr': /\bwindow\s*\.\s*nostr\b/,
  'globalThis.nostr': /\bglobalThis\s*\.\s*nostr\b/,
  fetch: /\b(?:window\s*\.\s*|globalThis\s*\.\s*)?fetch\s*\(/,
  XMLHttpRequest: /\b(?:new\s+)?XMLHttpRequest\s*\(/,
  WebSocket: /\b(?:new\s+)?WebSocket\s*\(/,
  localStorage: /\b(?:window\s*\.\s*)?localStorage\b/,
  sessionStorage: /\b(?:window\s*\.\s*)?sessionStorage\b/,
  indexedDB: /\b(?:window\s*\.\s*)?indexedDB\b/,
  'document.cookie': /\bdocument\s*\.\s*cookie\b/,
};

test('the page holds no browser authority: no network, no browser storage, no signer', { skip }, () => {
  const found = Object.entries(FORBIDDEN).filter(([, re]) => re.test(text)).map(([label]) => label);
  assert.deepEqual(found, [], 'forbidden surfaces in the page');
});

test('nothing in the page needs threads, workers or cross-origin isolation', { skip }, () => {
  const module = inlinedWasm();
  // The wasm defines its own, unshared memory: the memory section is there and no memory is imported.
  const sections = new Set();
  for (let at = 8; at < module.length;) {
    const id = module[at++];
    let size = 0, shift = 0, byte;
    do { byte = module[at++]; size |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80);
    sections.add(id);
    at += size;
  }
  assert.ok(sections.has(5), 'the module has its own memory section');
  assert.doesNotMatch(text, /SharedArrayBuffer|new Worker\(|mainScriptUrlOrBlob|crossOriginIsolated/);
  const policy = /http-equiv="Content-Security-Policy" content="([^"]+)"/.exec(text)[1].replaceAll('&#39;', "'");
  const directives = new Map(policy.split(';').map((part) => part.trim().split(/\s+/)).map(([name, ...values]) => [name, values]));
  assert.deepEqual(directives.get('script-src'), ["'unsafe-inline'", "'wasm-unsafe-eval'"], 'no blob: scripts, no eval');
  assert.deepEqual(directives.get('worker-src'), ["'none'"]);
});

test('the manifest template pins the file and says what the napplet requires', { skip }, () => {
  assert.equal(manifest.kind, 35129);
  const tags = new Map(manifest.tags.map((tag) => [tag[0], tag]));
  assert.equal(tags.get('d')[1], D_TAG);
  assert.deepEqual(tags.get('path'), ['path', '/index.html', sha256]);
  assert.deepEqual(manifest.tags.filter((tag) => tag[0] === 'requires').map((tag) => tag[1]).sort(), REQUIRES);
  // @napplet/vite-plugin's aggregate: sorted "<sha256> <path>\n" lines, hashed.
  const aggregate = createHash('sha256').update(`${sha256} /index.html\n`).digest('hex');
  assert.deepEqual(tags.get('x'), ['x', aggregate, 'aggregate']);
});