ArcformSovereign Skill
A local-first verification companion for the Arcform DID.comms API. Generate proof checksums, monitor live governance alerts, audit verdicts offline, and participate in Portal-to-Portal mesh consensus — all from your browser toolbar. Zero data collection. Zero external dependencies. Store-ready.
Extension File Manifest
manifest.jsonExtension configuration — Manifest V3, permissions, content scripts
canonical-hasher.jsProof checksum engine — byte-identical to server-side sendEnvelope/bubbleVerify
audit-store.jsLocal Audit Spine — IndexedDB persistence for all verification verdicts
mesh-store.jsMeshNode registry — P2P peer management with dynamic trust scoring
content-script.jsAttestation HUD — DOM scanner, badge injector, SPA mutation observer
background.jsService worker — verification dispatch, checkout handoff, alert routing
popup.htmlPanel markup — self-contained HTML with inlined CSS, 380px fixed width
popup.jsPanel controller — wires zones to audit-store, mesh-store, and background.js
icons/Extension icons — 16×16, 48×48, 128×128 PNG assets for browser + store display
4-Zone Panel Architecture
The extension panel is divided into four hyper-focused zones, each mapped to a distinct layer of the Arcform control plane.
Credentials & Identity Status
Top bar — stores api_key_id in chrome.storage.local. Live connection badge shows CONNECTED (green) when pointing to arcform-api.base44.app or production, DISCONNECTED (gray) when missing or invalid.
Core Metrics & Token Counter
Middle deck — pulls current platform consumption via /v1/did/status. Visual allocation bar for hash usage. High-visibility 'Top Up Credits' button opens the Arcform checkout via chrome.tabs.create for instant upgrade.
Zero-Knowledge Hashing Shield
Action area — drop-zone for raw document data. 'Generate Proof Checksum' button runs local crypto.subtle.digest('SHA-256', ...) using the canonical wire format. Raw text is purged from memory, checksum fires to /verify-attribute or /bubbleVerify.
Active Control Plane Notifications
Bottom spine — real-time listener mirroring #arcform-governance and #arcform-ledger alerts. Degraded verdicts (unauthorised_signer) flash an orange warning bar across the browser.
Canonical Hashing Specification
The proof checksum engine uses a deterministic, length-prefixed, pipe-delimited wire format that is byte-identical to the server-side implementation in sendEnvelope and bubbleVerify.
Wire Format
// Canonical wire structure
"{byte_length}:{value}|{byte_length}:{value}|..."
// Example
"5:ab0f3|44:did:arcform:7d0a...|44:did:arcform:9e1b...|2:51|24:2026-06-30T12:00:00.000Z"
1. envelope_hashString → trim → toLowerCase2. sender_didString → trim → toLowerCase3. recipient_didString → trim → toLowerCase4. payload_sizeMath.trunc(Number(x)) → String5. timestampString → trim (ISO 8601 UTC)Length prefix uses new TextEncoder().encode(f).length (UTF-8 byte length, not string length). Output is lowercase hex SHA-256 (64 characters).
Store Assets
Required assets for Chrome Web Store submission. Icons at three pixel boundaries plus the 1280×800 promotional screenshot.
Extension Icons — Required Sizes

16×16
Browser tab favicon
arcform-16.png
48×48
Extensions management page
arcform-48.png
128×128
Chrome Web Store listing
arcform-128.pngProduction note: Export the source image at each exact pixel boundary using any image editor or canvas script. Chrome rejects icons that don't match the declared dimensions precisely. Use PNG format with transparency.
Screenshots & Promotional Images

1280×800px · PNG · Dark theme · 4-zone panel showcase

440×280px · PNG · Store discovery tile

1400×560px · PNG · Featured banner
Chrome Web Store requirements: All downloads are resized to exact pixel dimensions via canvas before saving as PNG. Screenshot must be exactly 1280×800 (or 640×400). Small promo tile (440×280) is required for store listing. Large promo tile (1400×560) is optional but recommended for featured placement.
Alert Ingestion Pipeline
The missing link between the Arcform control plane and Zone D. A chrome.alarms-based polling loop that survives MV3 service worker suspension, delivers governance alerts in near-real-time, and fires native browser notifications for critical events.
Pipeline Flow
chrome.alarms.create
On service worker boot, a recurring alarm fires every 60 seconds. The alarm persists across popup open/close cycles.
pollAlerts()
Each alarm tick calls /usageAlerts with the stored api_key_id and last_alert_ts cursor. Only new events since the last poll are returned.
Merge & Cap
Incoming alerts are appended to the existing chrome.storage.local array. The buffer is capped at 50 entries (FIFO eviction) to bound memory.
Zone D Render
popup.js listens on chrome.storage.onChanged — any update to the alerts key triggers an immediate re-render of the Control Plane feed.
Native Notification
Critical-severity alerts additionally fire chrome.notifications.create with priority 2, surfacing outside the extension popup.
Architecture Diagram
// Alert Ingestion — Data Flow
┌─────────────────┐ every 60s ┌──────────────────┐
│ chrome.alarms │ ─────────────────▸ │ pollAlerts() │
│ (POLL_ALARM) │ │ │
└─────────────────┘ └────────┬─────────┘
│
POST /usageAlerts
{ api_key_id, since }
│
┌──────────▼─────────┐
│ Arcform API │
│ /usageAlerts │
└──────────┬─────────┘
│
{ alerts: [...] }
│
┌─────────────────┼─────────────────┐
▼ ▼
┌────────────────────┐ ┌─────────────────┐
│ chrome.storage │ │ chrome.notifs │
│ .local.alerts │ │ (critical only) │
└────────┬───────────┘ └─────────────────┘
│
storage.onChanged
│
┌────────▼───────────┐
│ popup.js Zone D │
│ live re-render │
└────────────────────┘
Design Decisions
Why chrome.alarms over WebSocket?
Service workers in MV3 are ephemeral — they can be terminated at any time. chrome.alarms survives worker restarts and guarantees the polling loop resumes. WebSockets would silently die on worker suspension.
Why 60-second interval?
Balances freshness against API rate limits. Governance alerts (key rotations, signer changes) are not sub-second events. 60s is well within the chrome.alarms minimum of 30s.
Why cap at 50 alerts?
chrome.storage.local has a 10MB quota. At ~200 bytes per alert, 50 entries ≈ 10KB — leaving 99.9% of quota for audit-store and mesh-store data.
Why cursor-based polling?
The last_alert_ts cursor ensures idempotent polling. Duplicate alerts are impossible because the server only returns events after the cursor timestamp.
Live Panel Preview
Interactive preview of the extension popup. Try linking an API key, generating a hash, and expanding the alert feed — this is exactly what the panel looks like at 380px in Chrome.
—
Envelopes
—
Verdicts
—
Identities
Local Audit Spine
Every verification result is persisted in the browser's IndexedDB — creating a private, offline-capable audit trail that never leaves the user's machine. No server round-trips for historical queries.
IndexedDB Schema — arcform_audit_spine.verdicts
envelope_hashstringPrimary key — the unique envelope identifier
statusstringCombined verdict tier: verified | partial | degraded | orphaned | unverified
verified_atISO 8601When the verification was performed
sender_didstringSender DID from the audit spine (nullable)
recipient_didstringRecipient DID from the audit spine (nullable)
identity_verdictstringIdentity layer result: ok | ok_historic | revoked_before_send | ...
proof_checksumstringServer-returned proof checksum (64-char hex)
anchor_statusstringXRPL anchor state: pending | signed | anchored | skipped | failed
local_checksum_matchbooleanDid the locally-computed checksum match the server's? null if not computed.
Offline Storage
All verdicts persisted in IndexedDB. No network required for historical audit.
Indexed Queries
Search by verdict tier, sender DID, or timestamp via native IndexedDB indices.
Cross-Check Flag
Each record stores local_checksum_match — did the local hash match the server's proof_checksum?
Temporal Index
by_timestamp index enables time-windowed compliance queries without scanning the full store.
Aggregate Stats
getAuditStats() returns instant counts across all verdict tiers for the HUD dashboard.
JSON Export
exportAuditSpine() dumps the full local database for backup, compliance, or mesh comparison.
// audit-store.js — Local Audit Spine (IndexedDB)
// Stores every verification result locally for offline audit.
// Zero server dependency. Private to the user's browser profile.
const DB_NAME = 'arcform_audit_spine';
const DB_VERSION = 1;
const STORE_NAME = 'verdicts';
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
const store = db.createObjectStore(STORE_NAME, { keyPath: 'envelope_hash' });
store.createIndex('by_verdict', 'status', { unique: false });
store.createIndex('by_timestamp', 'verified_at', { unique: false });
store.createIndex('by_sender', 'sender_did', { unique: false });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Record a verification result in the local audit spine.
* Overwrites previous result for the same envelope_hash.
*/
export async function recordVerdict(verdict) {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite');
tx.objectStore(STORE_NAME).put({
envelope_hash: verdict.envelope_hash,
status: verdict.status,
verified_at: verdict.verified_at || new Date().toISOString(),
sender_did: verdict.audit_spine?.sender_did || null,
recipient_did: verdict.audit_spine?.recipient_did || null,
identity_verdict: verdict.identity?.identity_verdict || null,
proof_checksum: verdict.proof_checksum || null,
anchor_status: verdict.audit_spine?.anchor_status || null,
local_checksum_match: verdict._local_checksum_match || null,
});
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
}
/**
* Query local audit spine by verdict tier.
* Returns array of stored verdict summaries.
*/
export async function queryByVerdict(status) {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly');
const index = tx.objectStore(STORE_NAME).index('by_verdict');
const request = index.getAll(status);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Get aggregate counts across all stored verdicts.
*/
export async function getAuditStats() {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly');
const store = tx.objectStore(STORE_NAME);
const stats = { total: 0, verified: 0, partial: 0, degraded: 0, orphaned: 0, unverified: 0 };
store.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
stats.total++;
stats[cursor.value.status] = (stats[cursor.value.status] || 0) + 1;
cursor.continue();
} else {
resolve(stats);
}
};
tx.onerror = () => reject(tx.error);
});
}
/**
* Export the full local audit spine as JSON.
* For compliance, backup, or cross-node comparison.
*/
export async function exportAuditSpine() {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly');
const request = tx.objectStore(STORE_NAME).getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}MeshNode Schema & P2P Protocol
Each peered Validation Node is stored locally as a MeshNode record. The trust score evolves dynamically through consensus rounds — nodes that consistently agree with local verdicts gain trust, divergent nodes are deprioritised.
IndexedDB Schema — arcform_mesh.nodes
node_idUUIDPrimary key — unique node identifier (crypto.randomUUID)
didstringThe Arcform DID of the peered node (unique index)
endpointURLHTTPS verification endpoint for cross-check requests
labelstringHuman-readable label for the node
trust_score0.0–1.0Dynamic trust score — increases on consensus agreement, decreases on divergence
statusstringactive | suspended | removed
peered_atISO 8601When the peer relationship was established
last_seenISO 8601Last successful consensus interaction
last_consensus_hashstringEnvelope hash from the most recent consensus round
verification_countnumberTotal consensus rounds participated in
consensus_failuresnumberTimes this node diverged from local verdict
P2P Handshake Protocol
Discovery
User visits /marketplace or receives a DID Document link. Extension detects Arcform-compatible node metadata.
Import
One-Click Peer triggers window.postMessage({ type: 'ARCFORM_PEER', node }) from the site to the extension's content script.
Registration
Content script forwards to background.js → registerNode() persists to IndexedDB with initial trust_score of 0.5.
Consensus
On next verification, runConsensusRound() queries all active nodes and adjusts trust scores based on verdict agreement.
Trust Score Dynamics
// Trust score adjustment per consensus round
Agreement: trust_score += 0.05 (capped at 1.0)
Divergence: trust_score -= 0.10 (floor at 0.0)
// Asymmetric by design
// A node must agree 2x for every 1 divergence to maintain score.
// 10 agreements = 0.5 → 1.0 (fully trusted)
// 3 divergences = 0.5 → 0.2 (rapidly deprioritised)
0.8 — 1.0
Trusted
0.4 — 0.79
Neutral
0.0 — 0.39
Suspect
// mesh-store.js — MeshNode Local Registry (IndexedDB)
// Stores peered Validation Nodes for Portal-to-Portal consensus.
// Nodes are discovered via the Arcform Marketplace or manual import.
const DB_NAME = 'arcform_mesh';
const DB_VERSION = 1;
const STORE_NAME = 'nodes';
function openMeshDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
const store = db.createObjectStore(STORE_NAME, { keyPath: 'node_id' });
store.createIndex('by_did', 'did', { unique: true });
store.createIndex('by_trust', 'trust_score', { unique: false });
store.createIndex('by_status', 'status', { unique: false });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Register a new MeshNode (peer) in the local registry.
* Called when a user clicks "Peer" on the Marketplace, or
* when a DID Document is imported via postMessage handshake.
*/
export async function registerNode(node) {
const db = await openMeshDB();
const record = {
node_id: node.node_id || crypto.randomUUID(),
did: node.did,
endpoint: node.endpoint,
label: node.label || 'Unnamed Node',
trust_score: node.trust_score || 0.5,
status: 'active',
peered_at: new Date().toISOString(),
last_seen: new Date().toISOString(),
last_consensus_hash: null,
verification_count: 0,
consensus_failures: 0,
};
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite');
tx.objectStore(STORE_NAME).put(record);
tx.oncomplete = () => resolve(record);
tx.onerror = () => reject(tx.error);
});
}
/**
* Update a node's trust score after a consensus round.
* Score increases on agreement, decreases on divergence.
* Range: 0.0 (untrusted) → 1.0 (fully trusted)
*/
export async function updateTrustScore(nodeId, agreed) {
const db = await openMeshDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite');
const store = tx.objectStore(STORE_NAME);
const getReq = store.get(nodeId);
getReq.onsuccess = () => {
const node = getReq.result;
if (!node) return reject(new Error('Node not found'));
const delta = agreed ? 0.05 : -0.1;
node.trust_score = Math.max(0, Math.min(1, node.trust_score + delta));
node.verification_count++;
if (!agreed) node.consensus_failures++;
node.last_seen = new Date().toISOString();
store.put(node);
};
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
}
/**
* List all active mesh nodes, sorted by trust score descending.
*/
export async function listActiveNodes() {
const db = await openMeshDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly');
const index = tx.objectStore(STORE_NAME).index('by_status');
const request = index.getAll('active');
request.onsuccess = () => {
const sorted = request.result.sort((a, b) => b.trust_score - a.trust_score);
resolve(sorted);
};
request.onerror = () => reject(request.error);
});
}
/**
* Run a consensus round: verify an envelope hash against all
* active mesh nodes and compare verdicts.
*/
export async function runConsensusRound(envelopeHash, localVerdict) {
const nodes = await listActiveNodes();
const results = [];
for (const node of nodes) {
try {
const res = await fetch(node.endpoint + '/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ envelope_hash: envelopeHash }),
});
const verdict = await res.json();
const agreed = verdict.status === localVerdict;
await updateTrustScore(node.node_id, agreed);
results.push({ node_id: node.node_id, did: node.did, verdict: verdict.status, agreed });
} catch {
results.push({ node_id: node.node_id, did: node.did, verdict: 'unreachable', agreed: false });
}
}
const agreeing = results.filter(r => r.agreed).length;
return {
envelope_hash: envelopeHash,
local_verdict: localVerdict,
nodes_checked: results.length,
consensus: agreeing === results.length ? 'unanimous' : agreeing > results.length / 2 ? 'majority' : 'divergent',
results,
};
}Attestation HUD
A content script injected into every page that detects Arcform envelope hashes in the DOM and renders inline trust badges showing the live verification verdict. Click any badge to open the full cryptographic breakdown.
Badge States
✓ VERIFIEDIntegrity valid + identity ok at send-time. Full trust.
◐ PARTIALIntegrity valid but identity ambiguous. Audit spine intact.
⚠ DEGRADEDIntegrity valid but key was revoked/rotated before send-time.
✗ ORPHANEDBubble record exists but no audit spine found.
? UNVERIFIEDNo records at all for this envelope hash.
Detection & Annotation Pipeline
DOM Scan
TreeWalker scans all text nodes for 16-char hex strings matching envelope_hash format.
Badge Inject
A non-intrusive trust badge is inserted next to each detected hash. Initial state: ⏳ CHECKING.
Background Verify
Each hash is sent to the service worker via chrome.runtime.sendMessage for API verification.
Verdict Render
Badge colour and text update to reflect the server verdict (verified/degraded/etc).
Audit Record
Result is persisted to the Local Audit Spine (IndexedDB) for offline historical access.
SPA Observe
MutationObserver watches for DOM changes — re-scans automatically on SPA navigation.
// content-script.js — Attestation HUD (DOM Overlay)
// Injected into web pages to detect and annotate Arcform envelope hashes.
// No data is exfiltrated — all processing is local + extension-internal.
import { computeProofChecksum } from './canonical-hasher.js';
const HASH_PATTERN = /\b[a-f0-9]{16}\b/g;
const BADGE_CLASS = 'arcform-trust-badge';
/**
* Scan the page DOM for potential envelope hashes.
* Matches 16-char lowercase hex strings (Arcform envelope_hash format).
*/
function scanForHashes() {
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null
);
const candidates = [];
let node;
while ((node = walker.nextNode())) {
const matches = node.textContent.match(HASH_PATTERN);
if (matches) {
matches.forEach(hash => {
candidates.push({ hash, node });
});
}
}
return candidates;
}
/**
* Create a non-intrusive trust badge next to a detected hash.
* Badge states: pending (gray), verified (green), degraded (orange),
* partial (yellow), orphaned (red), unverified (dim red).
*/
function createBadge(hash, parentNode) {
// Avoid duplicate badges
if (parentNode.parentElement?.querySelector(`.${BADGE_CLASS}[data-hash="${hash}"]`)) return;
const badge = document.createElement('span');
badge.className = BADGE_CLASS;
badge.dataset.hash = hash;
badge.style.cssText = `
display: inline-flex; align-items: center; gap: 4px;
margin-left: 6px; padding: 2px 8px; border-radius: 12px;
font-family: 'IBM Plex Mono', monospace; font-size: 9px;
font-weight: 600; letter-spacing: 0.05em; cursor: pointer;
border: 1px solid rgba(255,255,255,0.1);
background: rgba(12,15,20,0.9); color: rgba(160,174,192,0.8);
transition: all 0.2s ease;
`;
badge.textContent = '⏳ CHECKING...';
badge.addEventListener('click', () => {
chrome.runtime.sendMessage({ type: 'OPEN_HUD', hash });
});
parentNode.parentElement.insertBefore(badge, parentNode.nextSibling);
return badge;
}
/**
* Verify a hash via the extension's background service worker
* and update the badge with the verdict.
*/
async function verifyAndAnnotate(hash, badge) {
try {
const response = await chrome.runtime.sendMessage({
type: 'VERIFY_HASH',
envelope_hash: hash,
});
if (!response || response.error) {
updateBadgeState(badge, 'error');
return;
}
updateBadgeState(badge, response.status);
} catch {
updateBadgeState(badge, 'error');
}
}
const BADGE_STYLES = {
verified: { bg: 'rgba(16,185,129,0.15)', border: 'rgba(16,185,129,0.3)', color: '#10b981', text: '✓ VERIFIED' },
partial: { bg: 'rgba(234,179,8,0.15)', border: 'rgba(234,179,8,0.3)', color: '#eab308', text: '◐ PARTIAL' },
degraded: { bg: 'rgba(249,115,22,0.15)', border: 'rgba(249,115,22,0.3)', color: '#f97316', text: '⚠ DEGRADED' },
orphaned: { bg: 'rgba(239,68,68,0.15)', border: 'rgba(239,68,68,0.3)', color: '#ef4444', text: '✗ ORPHANED' },
unverified: { bg: 'rgba(239,68,68,0.10)', border: 'rgba(239,68,68,0.2)', color: '#ef4444', text: '? UNVERIFIED' },
error: { bg: 'rgba(100,100,100,0.15)', border: 'rgba(100,100,100,0.3)',color: '#888', text: '— ERROR' },
};
function updateBadgeState(badge, status) {
const style = BADGE_STYLES[status] || BADGE_STYLES.error;
badge.style.background = style.bg;
badge.style.borderColor = style.border;
badge.style.color = style.color;
badge.textContent = style.text;
}
/**
* Main entry point — scan page, create badges, verify each.
* Runs once on page load + observes DOM mutations for SPAs.
*/
async function init() {
const candidates = scanForHashes();
for (const { hash, node } of candidates) {
const badge = createBadge(hash, node);
if (badge) {
verifyAndAnnotate(hash, badge);
}
}
// Observe DOM mutations for SPA navigation
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const added of mutation.addedNodes) {
if (added.nodeType === Node.ELEMENT_NODE) {
const text = added.textContent || '';
const matches = text.match(HASH_PATTERN);
if (matches) {
// Re-scan on significant DOM changes
setTimeout(() => init(), 500);
return;
}
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
// Fire on DOMContentLoaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}// Add to manifest.json for HUD injection:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_idle"
}
],
"permissions": ["storage", "notifications", "activeTab"]Panel UI Source
The popup panel runs as a self-contained HTML document inside the Chrome extension. All styling is inlined (no external CSS dependencies). The JS module imports from the canonical hasher, audit store, and mesh store.
UI ↔ Module Wiring
Zone Achrome.storage.local → api_key_id persistence → updateConnectionBadge()
Zone BgetAuditStats() + listActiveNodes() + fetch(/didcommsStatus) → metric cards + progress bar
Zone CcomputeProofChecksum() or sha256Hex() → purge input → display result
Zone Dchrome.storage.onChanged listener → live alert rendering from background.js pushes
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arcform Skill</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 380px; min-height: 420px;
background: #06080B; color: #D4D8E0;
font-family: 'IBM Plex Mono', ui-monospace, monospace;
font-size: 10px;
}
/* Title bar */
.title-bar {
display: flex; align-items: center; justify-content: space-between;
padding: 8px 12px; background: #0A0D12;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.title-bar .brand { display: flex; align-items: center; gap: 6px; }
.title-bar .brand-icon {
width: 20px; height: 20px; border-radius: 4px;
background: rgba(0,199,217,0.15); border: 1px solid rgba(0,199,217,0.3);
display: flex; align-items: center; justify-content: center;
font-size: 8px; font-weight: 700; color: #00C7D9;
}
.title-bar .brand-name { font-size: 10px; font-weight: 700; color: #00C7D9; letter-spacing: 0.1em; }
.title-bar .brand-sub { font-size: 8px; color: #666; letter-spacing: 0.15em; margin-left: 4px; }
/* Connection badge */
.badge {
padding: 2px 8px; border-radius: 9999px;
font-size: 8px; font-weight: 700; letter-spacing: 0.08em;
}
.badge-connected { background: rgba(16,185,129,0.15); color: #10b981; border: 1px solid rgba(16,185,129,0.3); }
.badge-disconnected { background: rgba(255,255,255,0.05); color: #666; border: 1px solid rgba(255,255,255,0.1); }
/* Zones */
.zone { padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,0.05); }
.zone-label {
display: flex; align-items: center; gap: 4px;
font-size: 9px; color: #666; letter-spacing: 0.1em;
text-transform: uppercase; margin-bottom: 6px;
}
.zone-badge {
display: inline-flex; align-items: center; justify-content: center;
width: 16px; height: 16px; border-radius: 3px;
font-size: 8px; font-weight: 700; border: 1px solid currentColor;
opacity: 0.6;
}
/* Inputs */
.input-row { display: flex; gap: 6px; }
.text-input {
flex: 1; background: #14181E; border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px; padding: 4px 8px; font-size: 10px;
color: #D4D8E0; font-family: inherit; outline: none;
}
.text-input:focus { border-color: rgba(0,199,217,0.4); }
.text-input::placeholder { color: rgba(255,255,255,0.2); }
textarea.text-input { resize: none; width: 100%; }
/* Buttons */
.btn {
padding: 6px 10px; border-radius: 4px; font-size: 9px;
font-weight: 700; cursor: pointer; border: 1px solid;
transition: background 0.15s;
}
.btn-teal { background: rgba(0,199,217,0.1); border-color: rgba(0,199,217,0.2); color: #00C7D9; }
.btn-teal:hover { background: rgba(0,199,217,0.2); }
.btn-yellow { background: rgba(234,179,8,0.1); border-color: rgba(234,179,8,0.2); color: #eab308; }
.btn-yellow:hover { background: rgba(234,179,8,0.2); }
.btn-full { width: 100%; display: flex; align-items: center; justify-content: center; gap: 4px; margin-top: 6px; }
/* Metrics grid */
.metrics-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 8px; }
.metric-card {
text-align: center; padding: 6px; border-radius: 4px;
background: #14181E; border: 1px solid rgba(255,255,255,0.05);
}
.metric-value { font-size: 14px; font-weight: 700; color: #D4D8E0; }
.metric-label { font-size: 8px; color: #666; }
/* Progress bar */
.progress-row { display: flex; align-items: center; gap: 8px; }
.progress-track { flex: 1; height: 6px; border-radius: 3px; background: #14181E; overflow: hidden; }
.progress-fill { height: 100%; border-radius: 3px; background: linear-gradient(90deg, #00C7D9, rgba(0,199,217,0.6)); }
.progress-label { font-size: 8px; color: #666; flex-shrink: 0; }
/* Hash result */
.hash-result {
margin-top: 8px; padding: 8px; border-radius: 4px;
background: #14181E; border: 1px solid rgba(16,185,129,0.2);
}
.hash-result-label { font-size: 8px; font-weight: 700; color: #10b981; letter-spacing: 0.08em; margin-bottom: 4px; }
.hash-result-value { font-size: 9px; color: rgba(16,185,129,0.8); word-break: break-all; line-height: 1.6; }
/* Alerts */
.alert-item {
display: flex; align-items: flex-start; gap: 6px;
padding: 6px; border-radius: 4px; margin-bottom: 4px;
}
.alert-dot { width: 6px; height: 6px; border-radius: 50%; margin-top: 3px; flex-shrink: 0; }
.alert-dot-critical { background: #D90429; }
.alert-dot-warning { background: #f97316; }
.alert-dot-info { background: #00C7D9; }
.alert-critical { border: 1px solid rgba(217,4,41,0.3); background: rgba(217,4,41,0.05); }
.alert-warning { border: 1px solid rgba(249,115,22,0.3); background: rgba(249,115,22,0.05); }
.alert-info { border: 1px solid rgba(255,255,255,0.05); background: rgba(255,255,255,0.02); }
.alert-msg { flex: 1; font-size: 9px; color: rgba(255,255,255,0.7); }
.alert-time { font-size: 8px; color: #666; flex-shrink: 0; }
/* Footer */
.footer {
display: flex; align-items: center; justify-content: space-between;
padding: 6px 12px; background: #0A0D12;
border-top: 1px solid rgba(255,255,255,0.05);
font-size: 7px; color: rgba(255,255,255,0.25); letter-spacing: 0.1em;
}
</style>
</head>
<body>
<!-- Title Bar -->
<div class="title-bar">
<div class="brand">
<div class="brand-icon">A</div>
<span class="brand-name">ARCFORM</span>
<span class="brand-sub">SKILL</span>
</div>
<div id="connectionBadge" class="badge badge-disconnected">○ DISCONNECTED</div>
</div>
<!-- ZONE A: Credentials -->
<div class="zone">
<div class="zone-label"><span class="zone-badge" style="color:#10b981">A</span> Identity</div>
<div class="input-row">
<input type="text" id="apiKeyInput" class="text-input" placeholder="ak_prod_...">
<button id="saveKey" class="btn btn-teal">LINK</button>
</div>
</div>
<!-- ZONE B: Metrics -->
<div class="zone">
<div class="zone-label"><span class="zone-badge" style="color:#00C7D9">B</span> Metrics</div>
<div class="metrics-grid">
<div class="metric-card"><div id="metricEnvelopes" class="metric-value">—</div><div class="metric-label">Envelopes</div></div>
<div class="metric-card"><div id="metricVerdicts" class="metric-value">—</div><div class="metric-label">Verdicts</div></div>
<div class="metric-card"><div id="metricNodes" class="metric-value">—</div><div class="metric-label">Mesh Nodes</div></div>
</div>
<div class="progress-row">
<div class="progress-track"><div id="usageBar" class="progress-fill" style="width:0%"></div></div>
<span id="usageLabel" class="progress-label">—</span>
</div>
<button id="topUpBtn" class="btn btn-teal btn-full">↗ TOP UP CREDITS</button>
</div>
<!-- ZONE C: Hash Engine -->
<div class="zone">
<div class="zone-label"><span class="zone-badge" style="color:#eab308">C</span> Hash Engine</div>
<textarea id="hashInput" class="text-input" rows="2" placeholder="Paste envelope JSON or raw data..."></textarea>
<button id="hashBtn" class="btn btn-yellow btn-full">⚡ GENERATE PROOF CHECKSUM</button>
<div id="hashResultContainer" class="hash-result" style="display:none">
<div class="hash-result-label">SHA-256 OUTPUT</div>
<div id="hashResult" class="hash-result-value"></div>
</div>
</div>
<!-- ZONE D: Alerts -->
<div class="zone" style="border-bottom:none">
<div class="zone-label"><span class="zone-badge" style="color:#D90429">D</span> Control Plane</div>
<div id="alertsList"></div>
</div>
<!-- Footer -->
<div class="footer">
<span>LOCAL-FIRST · ZERO COLLECTION</span>
<span>v1.0.0</span>
</div>
<script type="module" src="popup.js"></script>
</body>
</html>// popup.js — Arcform Chrome Extension Panel Controller (v2)
// Wires all 4 zones to IndexedDB stores and background service worker.
import { computeProofChecksum, sha256Hex } from './canonical-hasher.js';
import { getAuditStats } from './audit-store.js';
import { listActiveNodes } from './mesh-store.js';
const API_BASE = 'https://arcform-api.com';
// ═══════════════════════════════════════════
// ZONE A: Credentials & Identity Status
// ═══════════════════════════════════════════
async function loadCredentials() {
const { api_key_id } = await chrome.storage.local.get('api_key_id');
if (api_key_id) {
document.getElementById('apiKeyInput').value = api_key_id;
updateConnectionBadge('connected');
loadMetrics(api_key_id);
} else {
updateConnectionBadge('disconnected');
}
}
async function saveCredentials() {
const apiKey = document.getElementById('apiKeyInput').value.trim();
if (!apiKey) return;
await chrome.storage.local.set({ api_key_id: apiKey });
updateConnectionBadge('connected');
loadMetrics(apiKey);
}
function updateConnectionBadge(status) {
const badge = document.getElementById('connectionBadge');
badge.textContent = status === 'connected' ? '● CONNECTED' : '○ DISCONNECTED';
badge.className = status === 'connected'
? 'badge badge-connected' : 'badge badge-disconnected';
}
// ═══════════════════════════════════════════
// ZONE B: Metrics & Token Counter
// ═══════════════════════════════════════════
async function loadMetrics(apiKey) {
// Load local audit stats
try {
const stats = await getAuditStats();
document.getElementById('metricVerdicts').textContent =
stats.total.toLocaleString();
} catch { }
// Load mesh node count
try {
const nodes = await listActiveNodes();
document.getElementById('metricNodes').textContent =
nodes.length.toString();
} catch { }
// Load server-side usage
try {
const res = await fetch(API_BASE + '/functions/didcommsStatus', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ api_key_id: apiKey }),
});
const data = await res.json();
if (data.usage_count !== undefined) {
document.getElementById('metricEnvelopes').textContent =
data.usage_count.toLocaleString();
const limit = data.message_limit || 10000;
const pct = Math.min(100, Math.round((data.usage_count / limit) * 100));
document.getElementById('usageBar').style.width = pct + '%';
document.getElementById('usageLabel').textContent =
data.usage_count.toLocaleString() + ' / ' + limit.toLocaleString();
}
} catch { }
}
// ═══════════════════════════════════════════
// ZONE C: Local Hashing Engine
// ═══════════════════════════════════════════
async function generateChecksum() {
const input = document.getElementById('hashInput').value;
if (!input) return;
const resultContainer = document.getElementById('hashResultContainer');
const resultEl = document.getElementById('hashResult');
resultContainer.style.display = 'block';
resultEl.textContent = '⏳ Computing...';
try {
let checksum;
try {
const parsed = JSON.parse(input);
if (parsed.envelope_hash && parsed.sender_did) {
checksum = await computeProofChecksum(
parsed.envelope_hash,
parsed.sender_did,
parsed.recipient_did,
parsed.payload_size_bytes,
parsed.timestamp
);
} else {
checksum = await sha256Hex(input);
}
} catch {
checksum = await sha256Hex(input);
}
resultEl.textContent = checksum;
document.getElementById('hashInput').value = '';
} catch (err) {
resultEl.textContent = 'Error: ' + err.message;
}
}
// ═══════════════════════════════════════════
// ZONE D: Control Plane Alerts
// ═══════════════════════════════════════════
async function loadAlerts() {
// Alerts are pushed from background.js via chrome.storage.local
const { alerts } = await chrome.storage.local.get('alerts');
const list = document.getElementById('alertsList');
const items = (alerts || []).slice(-5).reverse();
if (!items.length) {
list.innerHTML = '<div style="text-align:center;padding:8px;color:#444;font-size:8px">No recent alerts</div>';
return;
}
list.innerHTML = items.map(a => {
const sev = a.severity || 'info';
return `
<div class="alert-item alert-${sev}">
<div class="alert-dot alert-dot-${sev}"></div>
<div class="alert-msg">${a.message}</div>
<div class="alert-time">${a.time || ''}</div>
</div>
`;
}).join('');
}
// ═══════════════════════════════════════════
// Top Up Credits — opens checkout in new tab
// ═══════════════════════════════════════════
function handleTopUp() {
chrome.runtime.sendMessage({
type: 'OPEN_CHECKOUT',
checkoutUrl: 'https://arcform-api.base44.app/plans',
});
}
// ═══════════════════════════════════════════
// INIT
// ═══════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => {
loadCredentials();
loadAlerts();
document.getElementById('saveKey').addEventListener('click', saveCredentials);
document.getElementById('hashBtn').addEventListener('click', generateChecksum);
document.getElementById('topUpBtn').addEventListener('click', handleTopUp);
// Listen for storage changes (live alert updates)
chrome.storage.onChanged.addListener((changes) => {
if (changes.alerts) loadAlerts();
});
});Core Extension Source
{
"manifest_version": 3,
"name": "Arcform — Sovereign Verification Skill",
"version": "1.0.0",
"description": "Local-first identity verification and proof checksum generation for the Arcform DID.comms API.",
"permissions": ["storage", "notifications", "alarms"],
"host_permissions": [
"https://arcform-api.com/*"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/arcform-16.png",
"48": "icons/arcform-48.png",
"128": "icons/arcform-128.png"
}
},
"background": {
"service_worker": "background.js",
"type": "module"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_idle"
}
],
"icons": {
"16": "icons/arcform-16.png",
"48": "icons/arcform-48.png",
"128": "icons/arcform-128.png"
}
}// canonical-hasher.js — Arcform Proof Checksum Engine
// Runs entirely in browser via Web Crypto API (crypto.subtle)
// Zero dependencies. Zero network calls. Pure local computation.
export async function computeProofChecksum(
envelopeHash, senderDid, recipientDid, payloadBytes, timestamp
) {
const fields = [
String(envelopeHash).trim().toLowerCase(),
String(senderDid).trim().toLowerCase(),
String(recipientDid).trim().toLowerCase(),
String(Math.trunc(Number(payloadBytes))),
String(timestamp).trim(),
];
const spine = fields.map(f => {
const bytes = new TextEncoder().encode(f);
return `${bytes.length}:${f}`;
}).join('|');
const encoded = new TextEncoder().encode(spine);
const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
export async function sha256Hex(input) {
const encoded = new TextEncoder().encode(input);
const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}// background.js — Arcform Service Worker (v3 — Store Ready)
// Handles verification dispatch, checkout handoff, notifications,
// mesh consensus coordination, and alert polling pipeline.
import { computeProofChecksum } from './canonical-hasher.js';
import { recordVerdict } from './audit-store.js';
import { runConsensusRound } from './mesh-store.js';
const API_BASE = 'https://arcform-api.com';
const POLL_ALARM = 'arcform-alert-poll';
const POLL_INTERVAL_MINUTES = 1;
// ═══════════════════════════════════════════
// MESSAGE ROUTER
// ═══════════════════════════════════════════
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === 'VERIFY_HASH') {
handleVerify(msg.envelope_hash).then(sendResponse);
return true;
}
if (msg.type === 'OPEN_HUD') {
chrome.action.openPopup();
return true;
}
if (msg.type === 'OPEN_CHECKOUT') {
chrome.tabs.create({ url: msg.checkoutUrl, active: true });
sendResponse({ ok: true });
}
if (msg.type === 'SHOW_ALERT') {
pushAlert(msg);
sendResponse({ ok: true });
}
return true;
});
// ═══════════════════════════════════════════
// VERIFICATION HANDLER
// ═══════════════════════════════════════════
async function handleVerify(envelopeHash) {
try {
const { api_key_id } = await chrome.storage.local.get('api_key_id');
if (!api_key_id) return { error: 'No API key configured' };
const res = await fetch(API_BASE + '/functions/bubble', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ envelope_hash: envelopeHash }),
});
const verdict = await res.json();
// Record in local audit spine
await recordVerdict(verdict);
// Run mesh consensus (fire-and-forget)
runConsensusRound(envelopeHash, verdict.status).catch(() => {});
return verdict;
} catch (err) {
return { error: err.message, status: 'error' };
}
}
// ═══════════════════════════════════════════
// ALERT POLLING PIPELINE (chrome.alarms)
// Polls /functions/usageAlerts every 60s for
// governance, ledger, and audit events.
// Pushes to chrome.storage.local for Zone D.
// ═══════════════════════════════════════════
chrome.alarms.create(POLL_ALARM, {
delayInMinutes: 0.1,
periodInMinutes: POLL_INTERVAL_MINUTES,
});
chrome.alarms.onAlarm.addListener(async (alarm) => {
if (alarm.name !== POLL_ALARM) return;
await pollAlerts();
});
async function pollAlerts() {
try {
const { api_key_id, last_alert_ts } = await chrome.storage.local.get([
'api_key_id', 'last_alert_ts',
]);
if (!api_key_id) return;
const res = await fetch(API_BASE + '/functions/usageAlerts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key_id,
since: last_alert_ts || null,
}),
});
if (!res.ok) return;
const data = await res.json();
const incoming = data.alerts || [];
if (!incoming.length) return;
// Merge with existing alerts (keep last 50)
const { alerts: existing } = await chrome.storage.local.get('alerts');
const merged = [...(existing || []), ...incoming].slice(-50);
await chrome.storage.local.set({
alerts: merged,
last_alert_ts: incoming[incoming.length - 1].timestamp || new Date().toISOString(),
});
// Fire native notification for critical alerts
const critical = incoming.filter(a => a.severity === 'critical');
for (const alert of critical) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/arcform-48.png',
title: 'Arcform — Critical Alert',
message: alert.message,
priority: 2,
});
}
} catch { /* silent — retry on next alarm cycle */ }
}
// Helper: push a single alert to storage + optional notification
async function pushAlert(msg) {
const alert = {
message: msg.message,
severity: msg.severity || 'info',
time: new Date().toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }),
timestamp: new Date().toISOString(),
};
const { alerts: existing } = await chrome.storage.local.get('alerts');
const merged = [...(existing || []), alert].slice(-50);
await chrome.storage.local.set({ alerts: merged });
if (msg.severity === 'critical') {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/arcform-48.png',
title: msg.title || 'Arcform Alert',
message: msg.message,
priority: 2,
});
}
}
// ═══════════════════════════════════════════
// INSTALL HANDLER — first-run setup
// ═══════════════════════════════════════════
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
chrome.storage.local.set({ alerts: [], last_alert_ts: null });
}
})Chrome Web Store — Listing Specification
CategoryDeveloper Tools
LanguageEnglish (UK)
Permissionsstorage, notifications, alarms
Host Permissionshttps://arcform-api.com/*
ManifestV3 (latest)
PrivacyNo data collection. All hashing is local-only. IndexedDB stores verdicts locally.
Size< 60KB (zero external dependencies)
Local StorageIndexedDB: arcform_audit_spine (verdicts), arcform_mesh (peer nodes)
Alert Pollingchrome.alarms — 60s interval, /usageAlerts endpoint, critical → native notification
Chrome Web Store — Publishing Description
Copy-ready listing text for store submission
SHORT DESCRIPTION (132 CHARS)
Local-first identity verification and proof checksum generation for the Arcform DID.comms API. Zero data collection. Manifest V3.
FULL DESCRIPTION
Arcform — Sovereign Verification Skill
A developer-grade browser companion for the Arcform DID.comms API. Verify decentralised identity envelopes, generate cryptographic proof checksums, and monitor live governance alerts — all from your browser toolbar. Built for developers building on sovereign identity infrastructure.
🔐 What It Does
• Verify envelope integrity against the Arcform ledger in one click
• Generate SHA-256 proof checksums using the canonical wire format — byte-identical to the server-side implementation
• Monitor real-time governance and audit alerts from the Arcform control plane
• Maintain a local, tamper-evident audit trail of every verification performed (IndexedDB)
• Participate in Portal-to-Portal mesh consensus for cross-node validation
⚡ Key Features
• 4-Zone Panel — Identity status, usage metrics, zero-knowledge hashing, and control plane alerts in a single 380px popup
• Local-First Hashing — All cryptographic operations run in-browser via the Web Crypto API (crypto.subtle). No data leaves your machine
• Offline Audit Spine — Every verification verdict is recorded locally in IndexedDB. Query, export, or clear your audit trail at any time
• Live Alert Feed — Polls the Arcform API every 60 seconds for governance events, degraded verdicts, and ledger status changes. Critical alerts trigger native Chrome notifications
• Attestation HUD — Content script scans web pages for Arcform envelope hashes and injects real-time verification badges directly into the DOM
🛡️ Privacy & Security
• Zero data collection — no telemetry, no analytics, no tracking pixels
• All hashing is local-only. Raw input is never transmitted or stored
• IndexedDB audit data stays on your device — never synced to any server
• Zero external dependencies — under 60KB total extension size
• Open source. Every line of code is inspectable on the extension page
🚀 Getting Started
1. Install the extension from the Chrome Web Store
2. Click the Arcform icon in your browser toolbar
3. Paste your API key (ak_...) and click LINK
4. Start verifying envelopes, generating checksums, and monitoring alerts
🔗 Requirements
• An Arcform API key (free tier includes 1,000 messages/month)
• Chrome 110+ or any Chromium-based browser supporting Manifest V3
Built by Arcform Ltd — Sovereign Infrastructure for decentralised identity, encrypted messaging, and zero-knowledge attestations. Part of the Arcform DID.comms API ecosystem.
Documentation: arcform-api.base44.app/docs · Support: arcform-api.base44.app/support
Ready to Verify Locally?
The Arcform Chrome Skill is store-ready. Start building with the API today.