# Project export: AkuBrowser

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: A Browser (currently a plugin) that uses codex as an engine to read your social media and surfaces the content that you like. Bringing back the power of the algorithm to the hands of the user!
- Devpost: https://devpost.com/software/akubrowser
- GitHub: https://github.com/abangkis/AkuBrowser
- Video: https://www.youtube.com/embed/2449IWSdwYs?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — abangkis (105 commits)

## Devpost submission (written by the team)

### Inspiration

Attention is what everyone is fighting for right now. With the rise of For You Pages, every social platform is designed around an infinite feed. There is always another post, another notification, and another reason to keep scrolling. Access to information has improved, but consuming that information has become exhausting. I want to give control back to the user: the ability to decide which topics deserve their attention, which information they want to see more of, and when they have consumed enough. This is implemented by using cross-source semantic, to collapse similar content from difference author or source. And also a simple implementation if the content is AI generated or not. Lessen the burden of people who want to see AI generated content less. And as the Internet is changing constantly, browser it still the gateway to all the knowledge that available for human kind online. As we interact more and more with agent with MCP in the future a new kind of browser will be needed. Where the control is in the hand of the user not the server.

### What it does

Instead of passively accepting everything pushed into a timeline, AkuBrowser helps users filter and prioritize what is relevant to them. Its goal is not to replace the platforms or discard everything their recommendation systems have learned. AkuBrowser works with the user’s existing authenticated Chrome session. It treats the existing feed order as a borrowed behavioral prior, then builds a user-owned preference and reasoning layer on top of it. The desired experience is simple: Show me what matters to me, explain why it matters, preserve the sources, and let me feel finished. It currently supports X, Facebook and LinkedIn. A typical session: Captures a bounded set of posts from the user’s authenticated X, Facebook, and LinkedIn feeds. Validates each observation while preserving its metadata and original context. Skips unchanged posts or that has already been delivered. Uses Codex for Acquisition Planning. To determines whether the available evidence is sufficient or whether one additional bounded observation would be useful, Uses Codex for Candidate Evaluation. To describes each candidate’s topics, materiality, novelty, urgency, actionability, and evidence strength. Combines those assessments with the user’s explicit interests and locally learned preferences. Filters candidates according to the user’s attention policy. Uses Codex for cross-source semantic reasoning to recognize posts about the same event, collapse repeated reports, and preserve meaningful updates or different perspectives. Composes the selected events into one finite, personalized timeline. Applies fast AI detection and Deep AI detection (using Codex) to filter AI post to a dedicated drawer (configurable) Ends with a clear “End of catch-up” marker. AkuBrowser currently learns through explicit interaction rather than passive surveillance. Preference-based filtering and ranking are applied through deterministic application policy. The user remains the authority.

### How we built it

AkuBrowser is divided into these independent components: AkuBridge is a read-only Chrome extension. It performs bounded, read-only capture, source-specific quality checks and sends structured evidence to AkuSidecar. AkuSidecar is a local Golang application. It owns the UI, storage persistence (SQLite), session orchestration, deterministic selection, preference policy and managed Codex App Server process (Acquisition Planning, Candidate Evaluation, Semantic Event Resolution, Deep AI Detection). AkuSidecar is a local Golang application. It owns the UI, storage persistence (SQLite), session orchestration, deterministic selection, preference policy and managed Codex App Server process (Acquisition Planning, Candidate Evaluation, Semantic Event Resolution, Deep AI Detection). AkuBrowser is the main product and integration repository. It owns the architecture, canonical contracts, compatibility checks, Windows and macOS packaging, and aggregate development workflows across the components. AkuBrowser is the main product and integration repository. It owns the architecture, canonical contracts, compatibility checks, Windows and macOS packaging, and aggregate development workflows across the components. AkuSupervisor is an optional tool based Rust-based local lifecycle supervisor. It perfect to work with agentic workflow. It has MCP protocol that can determine the right time to start, stop and restart the plugins or the sidecar. The usage of token and computer usage can be reduced greatly with using AkuSupervisor in repeated development cycle. AkuSupervisor is an optional tool based Rust-based local lifecycle supervisor. It perfect to work with agentic workflow. It has MCP protocol that can determine the right time to start, stop and restart the plugins or the sidecar. The usage of token and computer usage can be reduced greatly with using AkuSupervisor in repeated development cycle. AkuSupervisor may eventually be bundled with AkuBrowser as the local lifecycle engine responsible for launching and maintaining AkuSidecar. What we built during OpenAI Build Week AkuBrowser started with the release of GPT 5.6. Only with the release of Sol that I started to believe it possible to implement this Idea without taking a huge amount of time. At least until the MVP stage. OpenAI Build Week gives the opportunity to push the development further. During the Build Week submission period, we materially extended it with the help of Codex across all repositories. The Build Week work includes: Implementing AkuSidecar and rewrite it from Node.js (because codex sdk only available to Node.js) to Go (transition to Codex App Server) while preserving SQLite state, recovery, and product behavior Integrating Codex App Server for Acquisition Planning, Candidate Evaluation, cross-source semantic resolution, and AI Deep Detection, profiles and usage telemetry Implementing preference-aware selection with calibration, More/Less feedback, local learning, candidate filtering, and correction controls Building a cross-source Semantic Event Engine that collapses repeated reports while preserving meaningful updates, contradictions, and perspectives Expanding bounded, source-faithful capture to X, LinkedIn, and Facebook with shared quality, freshness, media, and lifecycle controls Adding AI Fast and Deep Detection with user-controlled Inline, Drawer, and confirmed Hide modes; Strengthening onboarding, session recovery, exact-evidence suppression, resurfacing, reset, and Sidecar-restart behavior Packaging verifiable Windows x64 and macOS universal portable previews with native AkuSidecar executables, platform launchers, provenance, checksums, and the bundled AkuBridge extension Extending AkuSupervisor with lifecycle ownership, cooperative Bridge reload, health and log monitoring, read-only MCP support, and synchronized cross-repository tests. And many others. It's fully build using Codex and GPT 5.6 The dated Git history and BUILD_WEEK.md document which capabilities existed before the event and which were added or materially extended during the competition.

### Challenges we ran into

One of the hardest challenges was preserving source fidelity. Modern social sites are dynamic, and Chrome extension service workers can stop and restart. We built source-specific adapters, bounded retries, durable checkpoints, idempotent commands, and recovery paths so a partial capture or Sidecar restart does not corrupt the session. Preserving source fidelity was especially important to preserve the same consuming content feeling for the user. Preference filtering introduced another important challenge: information can be objectively material without being personally relevant. We needed to separate content understanding from user preference. Codex describes the content, while the user’s explicit interests and feedback determine how that content competes for their limited attention. And then implementation the cross-source semantic reasoning and AI filter to reduce the user burden to not ingest the same content or unwanted content. Security was equally important. Web content is untrusted evidence and may contain instructions intended to manipulate an AI agent. We isolates captured content from system instructions, limits the authority of the Reasoning Provider, validates every structured response, and keeps browser movement and filtering authority under deterministic policy. We also had to manage Codex token usage carefully. Development and runtime reasoning compete for the same limited resources. Finally, packaging a local system that spans Chrome, Go, SQLite, Codex, and a Rust supervisor is a product challenge of its own. We now provide clear portable entry points for Windows and macOS, while AkuSupervisor remains separate development tooling. A guided, signed installation and update experience remains future work.

### Accomplishments we're proud of

We are most proud that our original idea—giving control of attention back to the user—became an enforceable product rather than only a design promise. AkuBrowser combines bounded capture from authenticated X, Facebook, and LinkedIn feeds with structured Codex reasoning, user-owned preference filtering, cross-source semantic event resolution, knowledge continuity, AI Signals, and one finite Timeline. We also proud for being able to deliver a working prototype of a very ambitious and vague idea in couple of weeks. Tackling the technical issue, even re-writing AkuSidecar to a different Languange and protocol it become a doable task for better architecture. Things that previously could become a huge blocker now just a matter how many tokens can be spend. And thus a good token management become an important part in the development of this project.

### What we learned

We learned that AI works best when probabilistic reasoning is surrounded by deterministic boundaries. Codex can understand meaning, context, and evidence, but permissions, state and others must remain under application control. And development using AI doesn't mean you should let AI take the lead. It can lead you astray and spend token in places that is unnecessary and put you on difficult position when things need to be changed. A good token management is the key to deliver your project. We also learned that there's almost no boundary between the Idea you have and the possibility to implement it. Things that are difficult or take a very long time to implement could be done today. As long as you slice the specification correctly. It's worth to create a PoC to see is your idea actually make sense & feasible or not.

### What's next

AkuBrowser currently supports Windows x64 and macOS through a universal portable bundle for both Intel and Apple silicon, covering X, Facebook, and LinkedIn. Each package includes AkuBridge and a native AkuSidecar, while AkuSupervisor remains separate development tooling. Our next direction is to simplify installation and updates, expand to more social media and web sources, and eventually support Linux. The long-term goal is to rethink how people consume information in the age of Intelligence. Where information is abundant and attention is fought over. Website is not the only way to access information. With the rise of Agent and MCP protocol, how agent can be discovered and access, will be the new frontier. And browser seems to still be in the forefront as our access gate. Managing all the information provided by servers or agents on the internet will be the next challenge for Aku Browser. We might not be dependent on static representation from the server. But could dynamically build dependent on the user needs and like. There's also the economics side of monetary transaction and serving ads. It will be all handled by agent in the browser. But some human will also feel the needs to have a channel that preserve only for human interaction without an agent involvement. This is the challenge for future browser. And AkuBrowser could be the first generation of that future browser.

## README (from the GitHub repository)

# AkuBrowser

Current release target: **`v0.9.0-rc.8` for Windows x64**. Its planned distribution is one
unsigned installer `.exe` containing AkuBrowserLauncher,
AkuSidecar, the internal AkuBridge payload, pinned Chromium, c2patool, and the
release configuration/checksums needed by the isolated app shell. The release
must publish a SHA-256 checksum and explicitly disclose the unsigned SmartScreen
trust state. Independent clean-machine acceptance is still pending. The canonical
bundle boundary and gates are in the [installed-app distribution
contract](docs/installed-app-distribution-contract.md).

> **v0.9.0 RC8 distribution boundary.** The target release path is
> one Windows x64 installed-app installer. It does not require the Chrome Web
> Store, system Chrome, Developer Mode, a manually loaded extension, or a
> separate runtime installer. The current candidate still carries an honest
> unsigned/SmartScreen warning; users should verify the checksum of any published installer.
> macOS and Linux are deferred and are not part of this release.

> ### Chrome Web Store publication is frozen
>
> The existing Chrome Web Store listing remains available to current users, but
> no new Store builds are published. New production work targets the single
> installed-app tuple, with AkuSidecar owning first-run onboarding in the
> isolated app shell. The listing and portable ZIP are historical/recovery
> lanes, not v0.9.0 RC8 release outputs. See the [installed-app distribution
> target](docs/installed-app-distribution-contract.md).

## Release downloads

The next AkuBrowser `v0.9.0-rc.8` release
will expose one Windows x64 installer `.exe` and its SHA-256 checksum as the
supported download. It is intentionally unsigned, so Windows may show a
SmartScreen warning. Verify the checksum and do not run it alongside the frozen
Store edition.

The old Windows/macOS runtime installers and portable bundles remain versioned
historical/recovery assets. macOS and Linux downloads are explicitly deferred
from v0.9.0 RC8.

AkuBrowser turns bounded samples from the user's chosen social feeds into one finite, source-backed Timeline. The current source registry supports four adapters: X, LinkedIn, Facebook, and opt-in Instagram. It is designed for people who want to keep up without surrendering their attention to another infinite feed.

Its cross-author semantic Event Engine treats the underlying event—not the number of posts about it—as the unit of attention. When different authors or sources report the same specific occurrence, AkuBrowser can collapse the repetition while preserving the reports for inspection and correction. The user reads the change once instead of paying the same attention cost again for every account that repeated it.

Its AI Signals layer adds a separate, explicitly uncertain view of AI origin.
A local deterministic pass can annotate retained text immediately, while an
asynchronous Codex pass may confirm, dispute, or correct that preliminary
assessment. These signals never affect relevance selection or ranking. Drawer
is the preview default; users may instead keep strong signals inline or, after
an exact typed warning, hide only direct or Deep-confirmed results. Direct user
correction has the highest presentation authority.

X media can also finish passively after the usable Timeline is already on
screen. AkuBridge v60 keeps only short-lived, allowlisted post-media evidence
from the rendered page or X's already-requested timeline/detail responses and
may attach it to the matching retained item without making a provider request
or opening or focusing a tab. Raw responses and post text never leave the page
world or persist. Explicit quiet/foreground Recapture remains the fallback when
that evidence never appears.

The personalization rule is equally direct: explicit user feedback has more authority than opaque engagement inferred by a social network. Once the local profile has enough repeated evidence, More and Not interested may promote, replace, demote, or suppress ordinary candidates. Evidence quality, material updates, contradictions, and one bounded discovery lane remain protected.

## Distribution and workspace boundary

AkuBrowser is the distribution authority as well as the product-contract and
integration repository. It owns release manifests, installed-app installer
assembly, checksums, launchers, and acceptance guidance, while keeping
application runtime code in its component repositories. AkuBrowser has no Node
package or application runtime of its own.

### Historical shipped distribution (pre-v0.9.0)

The staged consumer distribution path publishes the extension under the public
name **AkuBrowser** while retaining `AkuBridge` as the internal component name.
Its Native Messaging, companion installer, Store, and signed runtime-update
gates are preserved as current implementation history in
[`docs/chrome-store-distribution-contract.md`](docs/chrome-store-distribution-contract.md).
Development and production extension IDs are named profiles in one registry;
their generated runtime projections are defined in
[`docs/bridge-identity-contract.md`](docs/bridge-identity-contract.md).

The pre-v0.9.0 end-user deployment had exactly two independently updated products:
AkuBridge from the Chrome Web Store and AkuSidecar from the signed platform
feed. The Native Messaging host is an internal helper shipped inside the
AkuSidecar installer, not a third deployable. AkuSupervisor remains optional
development tooling and is never required or shipped to Store users.

| Repository | Responsibility | Runtime |
| --- | --- | --- |
| `AkuBrowser` | Product contract, canonical schemas, distribution assembly, integration and artifact checks | PowerShell + POSIX shell release tooling |
| `AkuBridge` | Read-only bounded Chrome capture | Browser JavaScript / Node test tooling |
| `AkuSidecar` | UI, sessions, SQLite, reasoning, selection, personalization | Go |
| `AkuSupervisor` | Optional development-only process ownership; excluded from end-user deployment | Rust |

Only AkuBridge uses npm, because it is the Chrome extension. AkuSidecar is fully Go. In development AkuSupervisor may start Sidecar; production installation and update do not depend on Supervisor.

## Development and v0.9.0 RC8 release build

Run the workspace check from this repository:

```powershell
.\scripts\check.ps1
```

That command first runs a fail-fast integration identity check across the release manifest and the public Bridge/Sidecar declarations, then verifies schemas, runs Go tests in AkuSidecar, and runs npm checks inside AkuBridge. The identity check is development/release tooling only: AkuBridge and AkuSidecar do not import one another or read sibling repositories at runtime. It does not read AkuSupervisor configuration or require AkuSupervisor to be present. AkuBrowser itself does not install npm dependencies.

For normal local operation, build AkuSidecar and let AkuSupervisor own its lifecycle:

```powershell
cd ..\AkuSidecar
.\scripts\restart-dev.ps1
```

Open `http://127.0.0.1:11122` (or `http://localhost:11122`). Load
`..\AkuBridge` as an unpacked Chrome extension once; subsequent extension
reloads use the Sidecar's cooperative `reload_self` contract. AkuSupervisor
remains only the generic process owner.

The v0.9.0 RC8 installed app uses the bundled pinned Chromium profile and
does not require system Chrome or manual extension loading. Codex App with App
Server remains an external prerequisite and must be installed and signed in
locally. An optional Gemini provider accepts a user-supplied key through the
Sidecar credential flow; provider hot-swaps apply at an idle boundary. The four
source adapters are X, LinkedIn, Facebook, and opt-in Instagram. AkuSidecar
uses the schema declared in the current [release manifest](release/release-manifest.json)
and owns first-run onboarding, source readiness, and Timeline
state. See [Preview/release candidate notes](docs/preview-release.md).

The portable Z

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 67 recognized source files, 548 KB.
- Go (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Rust (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 122)

```
.github/workflows/windows-runtime-installer.yml
.gitignore
acceptance/windows-runtime-lifecycle.json
BUILD_WEEK.md
config/bridge-identities.json
contracts/acquisition-plan.schema.json
contracts/ai-deep-detection.schema.json
contracts/bridge-contract-v2.md
contracts/calibration-label.schema.json
contracts/calibration-profile-snapshot.schema.json
contracts/calibration-session.schema.json
contracts/examples/native-runtime-ensure-request.json
contracts/examples/native-runtime-invalid-arbitrary-action.json
contracts/examples/native-runtime-ready-response.json
contracts/examples/native-runtime-v1-ensure-request.json
contracts/examples/native-runtime-v1-ready-response.json
contracts/native-runtime-messaging-v1.schema.json
contracts/native-runtime-messaging.schema.json
contracts/README.md
contracts/reasoning-result.schema.json
contracts/runtime-update-manifest-v1.schema.json
contracts/runtime-update-manifest.schema.json
contracts/semantic-event-resolution.schema.json
docs/ai-feedback-contract.md
docs/auto-update-contract.md
docs/auto-update-guide.md
docs/bridge-identity-contract.md
docs/chrome-store-distribution-contract.md
docs/chrome-store-rollout-plan.md
docs/facebook-adapter-assessment.md
docs/facebook-adapter-cost-performance.md
docs/github-macos-signing-handoff.md
docs/instagram-adapter-assessment.md
docs/linkedin-adapter-cost-performance.md
docs/macos-clean-machine-3b.md
docs/macos-preview-acceptance.md
docs/macos-runtime-installer.md
docs/openai-build-week-submission.md
docs/preference-learning-contract.md
docs/preview-release.md
docs/product-contract.md
docs/releases/v0.8.0.md
docs/runtime-contract.md
docs/stable-release-checklist.md
docs/windows-clean-machine-3b.md
docs/windows-preview-acceptance.md
docs/windows-runtime-installer.md
docs/windows-runtime-lifecycle-acceptance.md
docs/windows-runtime-updater.md
installer/macos/Distribution.xml
installer/macos/resources/conclusion.html
installer/macos/resources/welcome-unsigned-preview.html
installer/macos/resources/welcome-unsigned-stable.html
installer/macos/resources/welcome.html
installer/macos/scripts/postinstall
installer/macos/scripts/preinstall
installer/macos/Uninstall-AkuBrowserRuntime.command
installer/windows/cmd/sign-update-manifest/main_test.go
installer/windows/cmd/sign-update-manifest/main.go
installer/windows/go.mod
installer/windows/go.sum
installer/windows/installer_test.go
installer/windows/installer.go
installer/windows/main_test.go
installer/windows/main.go
installer/windows/payload.go
installer/windows/payload/placeholder.txt
installer/windows/platform_other.go
installer/windows/platform_windows.go
installer/windows/setup.nsi
installer/windows/ui_other.go
installer/windows/ui_windows.go
LICENSE
PRIVACY.md
README.md
release/macos/README.md
release/macos/Start-AkuBrowser.command
release/macos/Start-AkuBrowser.sh
release/release-manifest.json
release/third-party/c2patool/LICENSE-MIT
release/third-party/c2patool/THIRD-PARTY-NOTICE.md
release/windows/README.md
release/windows/Start-AkuBrowser.cmd
release/windows/Start-AkuBrowser.ps1
scripts/bridge-extension-identity.mjs
scripts/build-chrome-store-package.ps1
scripts/build-macos-3b-acceptance-kit.sh
scripts/build-macos-preview.sh
scripts/build-macos-runtime-installer.sh
scripts/build-prestore-bridge-package.ps1
scripts/build-windows-preview.ps1
scripts/build-windows-runtime-installer.ps1
scripts/check-native-host-min-version.mjs
scripts/check-native-host-min-version.test.mjs
scripts/check-runtime-identity.mjs
scripts/check-runtime-identity.test.mjs
scripts/check.ps1
scripts/finalize-macos-signing-request.ps1
scripts/finalize-macos-signing.sh
scripts/fingerprint-extension-directory.mjs
scripts/generate-chrome-store-icons.go
scripts/prepare-local-release.ps1
scripts/project-bridge-package-identity.mjs
scripts/run-macos-signing-request.sh
scripts/run-macos-stable-gate.sh
scripts/run-windows-stable-gate.ps1
scripts/test-chrome-store-readiness.ps1
scripts/test-macos-preview.sh
scripts/test-macos-runtime-installer.sh
scripts/test-macos-signing-request.sh
scripts/test-windows-preview.ps1
scripts/test-windows-runtime-lifecycle.ps1
scripts/test-windows-runtime-updater.ps1
scripts/validate-bridge-identity-registry.mjs
scripts/validate-native-runtime-contracts.mjs
scripts/verify-release-tooling-drift.mjs
store/listing/en-US.md
store/listing/id-ID.md
store/permission-justification.md
store/privacy-declarations.json
[2 more files omitted for size]
```

### Dependencies

- installer/windows/go.mod: golang.org/x/sys@v0.19.0

### Recent commits (newest first)

- Align adapter performance runtime contract
- Align carousel runtime contract
- test: verify sidecar capture lifecycle
- docs: align reasoning defaults with runtime
- Add four AkuBrowser deployment identities
- Merge remote-tracking branch 'origin/main'
- Implement cross-platform runtime data reset lifecycle
- Merge remote-tracking branch 'Origin/main'
- docs: make Windows the final release publisher
- release: preserve tooling drift in Step 3B evidence
- release: allow Step 3B tooling drift
- release: separate macOS and Windows Step 3B kits
- fix: make macOS update checksums portable
- fix: include aligned legacy mac update assets
- release: preserve frozen payload across tooling drift
- split macOS signing handoff
- document GitHub macOS signing handoff
- make Windows own stable release drafts
- document Windows clean-machine acceptance runbook
- document DPAPI key entropy

## Key source files (fetched from GitHub, selected and truncated for size)

### PRIVACY.md

```markdown
# AkuBrowser Privacy Policy

Last updated: 7 August 2026

AkuBrowser's single purpose is to let a user build and personalize a private AI
feed from the X, LinkedIn, Facebook, and Instagram sources that the user explicitly
enables. The public Chrome extension is named **AkuBrowser**; its internal
extension component is called AkuBridge.

## Data AkuBrowser handles

For an enabled source, AkuBrowser may handle:

- rendered feed and post text;
- displayed account names, handles, profile images, and post authors;
- post URLs, source URLs, rendered timestamps, and source identity;
- displayed images, video metadata, and media URLs;
- the user's explicit AkuBrowser feedback, source selections, and settings;
- bounded operational state needed to complete a capture or recover the local
  runtime.

These categories include website content, browsing activity limited to the
enabled source pages, and user interaction with AkuBrowser. AkuBrowser does not
request social-account passwords, authentication cookies, payment information,
health information, or private-message access.

## Consent and source access

Social-source access is optional and no social-domain permission is granted by
default. The setup page initially suggests the supported sources, prominently
explains the data handled, and requires the user to confirm the selection before
Chrome shows its own permission request for the selected domains. AkuBrowser
registers source content scripts only after Chrome grants that permission.

The user may return to the extension's options page and revoke one or all source
permissions. Revocation unregisters that source's persistent content scripts.
AkuBrowser rejects capture commands for a source whose Chrome host permission is
not currently granted.

## How data is used

AkuBrowser uses source data only to:

- capture the user-requested source feed;
- evaluate, organize, deduplicate, and present items in the local AkuBrowser
  timeline;
- personalize selection using the user's explicit feedback;
- provide AI assessment and event grouping selected by the user;
- maintain security, compatibility, reliability, and bounded recovery of those
  features.

AkuBrowser does not use source data for advertising, credit decisions, data
brokerage, or an unrelated purpose.

## Storage and retention

The AkuBrowser companion runtime stores the timeline, settings, feedback, and
operational history in a local SQLite database under the current Windows user's
AkuBrowser data directory. The user chooses a 30, 60, or 90 day retention
boundary and a bounded storage cap.

The Chrome extension stores only bounded local operational state. X post-media
URL evidence expires after 30 minutes by default; X avatar URL evidence expires
after seven days by default. Runtime status, source-permission state, and
short-lived capture coordination are also stored in `chrome.storage.local`.

Uninstalling the Chrome extension clears Chrome-managed extension storage.
Uninstalling or repairing the co
[truncated — 2829 more characters]
```

### BUILD_WEEK.md

```markdown
# AkuBrowser - OpenAI Build Week evidence

Status: **submission-ready preview**
Current preview: **`0.7.0-preview.3`**
Supported platforms: **Windows x64; macOS x64 and arm64**
Supported sources: **X, Facebook, and LinkedIn**

This document connects the component repositories, distinguishes the
pre-existing prototype from Build Week extensions, describes how Codex and
GPT-5.6 contributed, and gives judges a path to run the project without
rebuilding it. The canonical story is
[`docs/openai-build-week-submission.md`](docs/openai-build-week-submission.md).

## Product direction

AkuBrowser is a finite, user-steered layer over infinite feeds. It combines
bounded capture from the user's authenticated Chrome session with structured
Codex reasoning, deterministic preference filtering, cross-source semantic
event resolution, knowledge continuity, AI Signals, and one finite Timeline.

The authority boundary is deliberate: AkuBridge observes, Codex plans and
evaluates, AkuSidecar owns policy and state, and the user remains the final
authority. AI Signals are a separate presentation layer and never silently own
relevance selection or ranking.

## Repository map

All repositories carry the Apache License 2.0.

| Repository | Build Week role | Runtime |
| --- | --- | --- |
| [AkuBrowser](https://github.com/abangkis/AkuBrowser) | Product contracts, schemas, release authority, Windows and macOS packaging, aggregate checks, final story, and this evidence | PowerShell + POSIX shell |
| [AkuBridge](https://github.com/abangkis/AkuBridge) | Bounded, read-only Manifest V3 capture for X, LinkedIn, and Facebook | Chrome JavaScript |
| [AkuSidecar](https://github.com/abangkis/AkuSidecar) | UI, SQLite, sessions, preference filtering, semantic events, AI Signals, and Codex App Server integration | Go |
| [AkuSupervisor](https://github.com/abangkis/AkuSupervisor) | Optional development lifecycle ownership, health, logs, cooperative Bridge reload, and read-only MCP inspection | Rust |
| [AkuSupervisorConformance](https://github.com/abangkis/AkuSupervisorConformance) | Optional lifecycle compatibility laboratory; supporting evidence only, not an AkuBrowser runtime component | Rust |

The portable preview contains AkuSidecar, AkuBridge, release configuration,
launchers, provenance, and SHA-256 checksums. AkuSupervisor and
AkuSupervisorConformance are intentionally excluded from the current end-user
bundle. Bundling Supervisor as a local lifecycle engine is future work.

## Pre-existing work and Build Week extensions

Before the submission period, AkuBrowser was an early bounded X/LinkedIn
prototype. The project was materially extended after the submission period
opened. The dated Git history is the audit trail; the examples below are not a
claim that every line in every commit was generated by Codex.

The Build Week work includes:

- rewriting AkuSidecar from Node.js to Go while preserving SQLite state,
  recovery, and product behavior (`bd6d78a`, `72460a1`);
- integrating C
[truncated — 5922 more characters]
```

### installer/windows/go.mod

```
module github.com/abangkis/AkuBrowser/installer/windows

go 1.21

require golang.org/x/sys v0.19.0

```

### installer/windows/main.go

```go
package main

import (
	"embed"
	"errors"
	"flag"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
)

//go:embed payload
var embeddedPayload embed.FS

func main() {
	os.Exit(runInstaller())
}

func runInstaller() int {
	var (
		repair              = flag.Bool("repair", false, "repair the current AkuBrowser Runtime installation")
		uninstall           = flag.Bool("uninstall", false, "uninstall AkuBrowser Runtime while preserving user data")
		fullReset           = flag.Bool("full-reset", false, "remove user data during uninstall")
		quiet               = flag.Bool("quiet", false, "suppress completion UI")
		installRoot         = flag.String("install-root", "", "absolute AkuBrowser Runtime installation directory")
		externalUninstaller = flag.Bool("external-uninstaller", false, "let an outer setup wizard own Add or Remove Programs registration")
	)
	flag.Parse()
	if *repair && *uninstall {
		finish(*quiet, errors.New("repair and uninstall cannot be requested together"))
		return 2
	}
	if *fullReset && !*uninstall {
		finish(*quiet, errors.New("full reset requires uninstall"))
		return 2
	}
	payload, err := fs.Sub(embeddedPayload, "payload")
	if err != nil {
		finish(*quiet, err)
		return 2
	}
	manifest, err := loadPayloadManifest(payload)
	if err != nil {
		finish(*quiet, err)
		return 2
	}
	localAppData := os.Getenv("LOCALAPPDATA")
	if localAppData == "" {
		finish(*quiet, errors.New("LOCALAPPDATA is unavailable"))
		return 2
	}
	resolvedInstallRoot, err := resolveInstallRoot(localAppData, *installRoot)
	if err != nil {
		finish(*quiet, err)
		return 2
	}
	executable, err := os.Executable()
	if err != nil {
		finish(*quiet, err)
		return 2
	}
	installer := Installer{
		Payload:                     payload,
		Manifest:                    manifest,
		InstallRoot:                 resolvedInstallRoot,
		DataRoot:                    filepath.Join(localAppData, "AkuBrowser", "data"),
		SourceExecutable:            executable,
		Registry:                    WindowsRegistry{},
		SkipUninstallerRegistration: *externalUninstaller,
	}
	action := "installed"
	if *uninstall {
		action = "uninstalled"
		if *fullReset {
			action = "fully reset"
		}
		err = installer.Uninstall(*fullReset)
	} else {
		if *repair {
			action = "repaired"
		}
		err = installer.Install()
	}
	if err != nil {
		finish(*quiet, err)
		return 1
	}
	finish(*quiet, nil, completionMessage(action))
	return 0
}

func resolveInstallRoot(localAppData, requested string) (string, error) {
	if strings.TrimSpace(requested) == "" {
		return defaultInstallRoot(localAppData), nil
	}
	if !filepath.IsAbs(requested) {
		return "", errors.New("installation directory must be an absolute Windows path")
	}
	return filepath.Clean(requested), nil
}

func completionMessage(action string) string {
	message := fmt.Sprintf("AkuBrowser Runtime %s successfully.", action)
	if action == "uninstalled" || action == "fully reset" {
		return message
	}
	return message + "\n\nReturn to Chrome and select Check runtime in AkuBrowser Setup. " +
		"If an older portable AkuBrowser Runtime is still running, close it first."
}

```

### installer/windows/cmd/sign-update-manifest/main.go

```go
package main

import (
	"bytes"
	"crypto/ed25519"
	"encoding/base64"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"net/url"
	"os"
	"path"
	"regexp"
	"strings"
	"time"
)

type artifactV1 struct {
	URL    string `json:"url"`
	Size   int64  `json:"size"`
	SHA256 string `json:"sha256"`
}

type artifactV2 struct {
	Platform string `json:"platform"`
	URL      string `json:"url"`
	Size     int64  `json:"size"`
	SHA256   string `json:"sha256"`
}

type bridgeCompatibility struct {
	Protocol             string   `json:"protocol"`
	MinVersion           int      `json:"minVersion"`
	MaxVersion           int      `json:"maxVersion"`
	RequiredCapabilities []string `json:"requiredCapabilities"`
}

type databaseCompatibility struct {
	MinSchemaVersion int  `json:"minSchemaVersion"`
	MaxSchemaVersion int  `json:"maxSchemaVersion"`
	RollbackSafe     bool `json:"rollbackSafe"`
}

var versionPattern = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$`)
var runtimeRevisionPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{2,79}$`)
var capabilityPattern = regexp.MustCompile(`^[a-z][a-z0-9._-]{1,63}$`)
var sha256Pattern = regexp.MustCompile(`^[a-f0-9]{64}$`)

type unsignedManifestV1 struct {
	SchemaVersion         int        `json:"schemaVersion"`
	Product               string     `json:"product"`
	Channel               string     `json:"channel"`
	Version               string     `json:"version"`
	RuntimeRevision       string     `json:"runtimeRevision"`
	BridgeContractVersion string     `json:"bridgeContractVersion"`
	PublishedAt           string     `json:"publishedAt"`
	Artifact              artifactV1 `json:"artifact"`
}

type unsignedManifestV2 struct {
	SchemaVersion         int                   `json:"schemaVersion"`
	Product               string                `json:"product"`
	Channel               string                `json:"channel"`
	SidecarVersion        string                `json:"sidecarVersion"`
	RuntimeRevision       string                `json:"runtimeRevision"`
	MinHostVersion        string                `json:"minHostVersion"`
	BridgeCompatibility   bridgeCompatibility   `json:"bridgeCompatibility"`
	DatabaseCompatibility databaseCompatibility `json:"databaseCompatibility"`
	PublishedAt           string                `json:"publishedAt"`
	Urgency               string                `json:"urgency,omitempty"`
	Deadline              string                `json:"deadline,omitempty"`
	Artifact              artifactV2            `json:"artifact"`
}

type signature struct {
	Algorithm string `json:"algorithm"`
	KeyID     string `json:"keyId"`
	Value     string `json:"value"`
}

type signedManifestV1 struct {
	SchemaVersion         int        `json:"schemaVersion"`
	Product               string     `json:"product"`
	Channel               string     `json:"channel"`
	Version               string     `json:"version"`
	RuntimeRevision       string     `json:"runtimeRevision"`
	BridgeContractVersion string     `json:"bridgeContractVersion"`
	PublishedAt           string     `json:"publishedAt"`
	Artifact              artifactV1 `json:"artifact"`
	Signature             signature  `json:"signature"`
}

type signedManifestV2 struct {
	SchemaVersion         int                   `json:"schemaVersion"`
	Product               string                `json:"product"`
	Channel               string                `json:"channel"`
	SidecarVersion        string                `json:"sidecarVersion"`
	RuntimeRevision       string                `json:"runtimeRevision"`
	MinHostVersion        string                `json:"minHostVersion"`
	BridgeCompatibility   bridgeCompatibility   `json:"bridgeCompatibility"`
	DatabaseCompatibility databaseCompatibility `json:"databaseCompatibility"`
	PublishedAt           string                `json:"publishedAt"`
	Urgency               string                `json:"urgency,omitempty"`
	Deadline              string                `json:"deadline,omitempty"`
	Artifact              artifactV2            `json:"artifact"`
	Signature             signature             `json:"signature"`
}

func main() {
	var manifestPath, privateKeyPath, outputPath, signedManifestPath, publicKeyText string
	flag.StringVar(&manifestPath, "manifest", "", "path to the unsigned canonical update manifest")
	flag.StringVar(&privateKeyPath, "private-key", "", "path containing one base64 Ed25519 seed or private key")
	flag.StringVar(&outputPath, "output", "", "path for the signed manifest")
	flag.StringVar(&signedManifestPath, "verify-signed", "", "path to a signed update manifest to verify")
	flag.StringVar(&publicKeyText, "public-key", "", "base64 Ed25519 public key used with -verify-signed")
	flag.Parse()
	if signedManifestPath != "" {
		if err := verifySigned(signedManifestPath, publicKeyText); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		return
	}
	if err := run(manifestPath, privateKeyPath, outputPath); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func verifySigned(manifestPath, publicKeyText string) error {
	if manifestPath == "" || strings.TrimSpace(publicKeyText) == "" {
		return errors.New("verify-signed and public-key are required")
	}
	data, err := os.ReadFile(manifestPath)
	if err != nil {
		return err
	}
	publicKey, err := base64.StdEncoding.DecodeString(strings.TrimSpace(publicKeyText))
	if err != nil || len(publicKey) != ed25519.PublicKeySize {
		return errors.New("update verification key must be a base64 32-byte Ed25519 public key")
	}
	var header struct {
		SchemaVersion int `json:"schemaVersion"`
	}
	if err := json.Unmarshal(data, &header); err != nil {
		return fmt.Errorf("decode signed manifest: %w", err)
	}
	switch header.SchemaVersion {
	case 1:
		var signed signedManifestV1
		if err := decodeStrict(data, &signed); err != nil {
			return err
		}
		if signed.SchemaVersion != 1 || signed.Product != "AkuBrowser" ||
			signed.Signature.Algorithm != "ed25519" || signed.Signature.KeyID != "aku-runtime-stable-v1" {
			return errors.N
[truncated — 8876 more characters]
```

### scripts/run-macos-stable-gate.sh

```shell
#!/usr/bin/env bash
set -euo pipefail

# Compatibility entry point. The stable Mac pass now stops at the
# Windows-signing handoff and never accepts a private update key.
browser_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
exec "$browser_root/scripts/run-macos-signing-request.sh" "$@"

```

### scripts/generate-chrome-store-icons.go

```go
package main

import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"os"
	"path/filepath"
)

var (
	transparent = color.NRGBA{0, 0, 0, 0}
	navy        = color.NRGBA{23, 32, 51, 255}
	blue        = color.NRGBA{70, 102, 235, 255}
	cyan        = color.NRGBA{54, 211, 190, 255}
	white       = color.NRGBA{255, 255, 255, 255}
)

func main() {
	browserRoot, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if len(os.Args) > 1 {
		browserRoot, err = filepath.Abs(os.Args[1])
		if err != nil {
			panic(err)
		}
	}
	workspaceRoot := filepath.Dir(browserRoot)
	bridgeIcons := filepath.Join(workspaceRoot, "AkuBridge", "icons")
	storeAssets := filepath.Join(browserRoot, "store", "assets")
	for _, directory := range []string{bridgeIcons, storeAssets} {
		if err := os.MkdirAll(directory, 0o755); err != nil {
			panic(err)
		}
	}
	for _, size := range []int{16, 32, 48, 128} {
		target := filepath.Join(bridgeIcons, fmt.Sprintf("icon-%d.png", size))
		if err := writePNG(target, renderIcon(size)); err != nil {
			panic(err)
		}
	}
	if err := writePNG(filepath.Join(storeAssets, "store-icon-128.png"), renderIcon(128)); err != nil {
		panic(err)
	}
}

func renderIcon(size int) image.Image {
	const scale = 4
	canvasSize := size * scale
	canvas := image.NewNRGBA(image.Rect(0, 0, canvasSize, canvasSize))
	fill(canvas, transparent)
	margin := float64(canvasSize) * 0.055
	roundedRect(canvas, margin, margin, float64(canvasSize)-margin, float64(canvasSize)-margin, float64(canvasSize)*0.225, navy)

	left := point{float64(canvasSize) * 0.285, float64(canvasSize) * 0.745}
	apex := point{float64(canvasSize) * 0.50, float64(canvasSize) * 0.245}
	right := point{float64(canvasSize) * 0.745, float64(canvasSize) * 0.745}
	width := float64(canvasSize) * 0.115
	thickLine(canvas, left, apex, width, blue)
	thickLine(canvas, apex, right, width, cyan)
	thickLine(
		canvas,
		point{float64(canvasSize) * 0.385, float64(canvasSize) * 0.585},
		point{float64(canvasSize) * 0.635, float64(canvasSize) * 0.585},
		float64(canvasSize)*0.075,
		white,
	)
	return downsample(canvas, size)
}

type point struct{ x, y float64 }

func thickLine(target *image.NRGBA, start, end point, width float64, value color.NRGBA) {
	minX := int(min(start.x, end.x) - width)
	maxX := int(max(start.x, end.x) + width)
	minY := int(min(start.y, end.y) - width)
	maxY := int(max(start.y, end.y) + width)
	for y := minY; y <= maxY; y++ {
		for x := minX; x <= maxX; x++ {
			if distanceToSegment(point{float64(x) + 0.5, float64(y) + 0.5}, start, end) <= width/2 {
				target.SetNRGBA(x, y, value)
			}
		}
	}
}

func distanceToSegment(value, start, end point) float64 {
	dx, dy := end.x-start.x, end.y-start.y
	lengthSquared := dx*dx + dy*dy
	if lengthSquared == 0 {
		return distance(value, start)
	}
	t := ((value.x-start.x)*dx + (value.y-start.y)*dy) / lengthSquared
	t = max(0, min(1, t))
	return distance(value, point{start.x + t*dx, start.y + t*dy})
}

func distance(a, b point) float64 {
	dx, dy := a.x-b.x, a.y-b.y
	return sqrt(dx*dx + dy*dy)
}

func roundedRect(target *image.NRGBA, left, top, right, bottom, radius float64, value color.NRGBA) {
	for y := int(top); y < int(bottom); y++ {
		for x := int(left); x < int(right); x++ {
			nearestX := max(left+radius, min(right-radius, float64(x)+0.5))
			nearestY := max(top+radius, min(bottom-radius, float64(y)+0.5))
			if distance(point{float64(x) + 0.5, float64(y) + 0.5}, point{nearestX, nearestY}) <= radius {
				target.SetNRGBA(x, y, value)
			}
		}
	}
}

func downsample(source *image.NRGBA, size int) *image.NRGBA {
	const scale = 4
	target := image.NewNRGBA(image.Rect(0, 0, size, size))
	for y := 0; y < size; y++ {
		for x := 0; x < size; x++ {
			var red, green, blueValue, alpha uint32
			for sampleY := 0; sampleY < scale; sampleY++ {
				for sampleX := 0; sampleX < scale; sampleX++ {
					value := source.NRGBAAt(x*scale+sampleX, y*scale+sampleY)
					red += uint32(value.R)
					green += uint32(value.G)
					blueValue += uint32(value.B)
					alpha += uint32(value.A)
				}
			}
			divisor := uint32(scale * scale)
			target.SetNRGBA(x, y, color.NRGBA{
				R: uint8(red / divisor), G: uint8(green / divisor),
				B: uint8(blueValue / divisor), A: uint8(alpha / divisor),
			})
		}
	}
	return target
}

func fill(target *image.NRGBA, value color.NRGBA) {
	for y := target.Bounds().Min.Y; y < target.Bounds().Max.Y; y++ {
		for x := target.Bounds().Min.X; x < target.Bounds().Max.X; x++ {
			target.SetNRGBA(x, y, value)
		}
	}
}

func writePNG(path string, value image.Image) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()
	return png.Encode(file, value)
}

func sqrt(value float64) float64 {
	if value == 0 {
		return 0
	}
	estimate := value
	for index := 0; index < 12; index++ {
		estimate = (estimate + value/estimate) / 2
	}
	return estimate
}

```

### scripts/test-macos-signing-request.sh

```shell
#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
Usage: ./scripts/test-macos-signing-request.sh --request <zip> --assets-root <directory> --public-key <base64>
EOF
}

die() { echo "error: $*" >&2; exit 1; }

request_zip=""
assets_root=""
public_key=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --request) request_zip="$2"; shift 2 ;;
    --assets-root) assets_root="$2"; shift 2 ;;
    --public-key) public_key="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) die "unknown argument: $1" ;;
  esac
done

[[ -f "$request_zip" ]] || die "signing request ZIP is missing: $request_zip"
[[ -d "$assets_root" ]] || die "publish asset directory is missing: $assets_root"
[[ -n "$public_key" ]] || die "--public-key is required"

for command_name in unzip node shasum; do
  command -v "$command_name" >/dev/null 2>&1 || die "required command is unavailable: $command_name"
done

inspect_root="$(mktemp -d "${TMPDIR:-/tmp}/akubrowser-macos-signing-request-test.XXXXXX")"
trap 'rm -rf -- "$inspect_root"' EXIT
unzip -q "$request_zip" -d "$inspect_root/request"

[[ -f "$inspect_root/request/signing-request.json" ]] || die "signing request metadata is missing"
[[ -f "$inspect_root/request/release-manifest.json" ]] || die "release manifest is missing from signing request"
[[ -f "$inspect_root/request/portable-artifact-manifest.json" ]] || die "portable artifact manifest is missing from signing request"
[[ -f "$inspect_root/request/browser-tooling-drift.json" ]] || die "AkuBrowser tooling-drift receipt is missing"

if find "$inspect_root/request" -type f \( -name '*.dpapi' -o -name '*.key' -o -name '*private*' -o -name '*secret*' \) -print -quit | grep -q .; then
  die "signing request contains a private-key-like file"
fi

node --input-type=module - "$inspect_root/request" "$assets_root" "$public_key" <<'NODE'
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";

const [requestRoot, assetsRoot, expectedPublicKey] = process.argv.slice(2);
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
const fail = (message) => { throw new Error(message); };
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
const safeName = (value, label) => {
  if (typeof value !== "string" || value.length === 0 || value !== path.basename(value) || value.includes("\\")) {
    fail(`${label} must be a single relative filename: ${value}`);
  }
  return value;
};
const record = (file) => ({
  bytes: fs.statSync(file).size,
  sha256: sha256(file),
});

const request = readJson(path.join(requestRoot, "signing-request.json"));
const toolingDriftPath = path.join(requestRoot, "browser-tooling-drift.json");
const toolingDrift = readJson(toolingDriftPath);
if (request.schemaVersion !== 1 || request.kind !== "AkuBrowser.macos-signing-request" || request.status !== "unsigned") {
  fail("signing request identity is invalid");
}
if (!/^\d+\.\d+\.\d+$/.test(request.releaseVersion) || !/^\d+\.\d+\.\d+$/.test(request.sidecarVersion)) {
  fail("signing request versions are invalid");
}
for (const [name, sha] of Object.entries(request.sourceCommits ?? {})) {
  if (!/^[a-f0-9]{40}$/.test(sha)) fail(`source commit is invalid: ${name}`);
}
if (!/^[a-f0-9]{40}$/.test(request.toolingCommits?.akuBrowser ?? "") ||
    toolingDrift.schemaVersion !== 1 || toolingDrift.status !== "ok" || toolingDrift.workingTreeDirty !== false ||
    !["none", "release-tooling-only"].includes(toolingDrift.kind) ||
    toolingDrift.releaseSourceSha !== request.sourceCommits.akuBrowser ||
    toolingDrift.toolingSha !== request.toolingCommits.akuBrowser ||
    request.provenance?.browserToolingDriftSha256 !== sha256(toolingDriftPath)) {
  fail("AkuBrowser release/tooling provenance is invalid");
}
if (request.publicKey?.algorithm !== "Ed25519" || request.publicKey?.keyId !== "aku-runtime-stable-v1" ||
    request.publicKey?.base64 !== expectedPublicKey || Buffer.from(expectedPublicKey, "base64").length !== 32) {
  fail("signing request public key metadata is invalid");
}

const assets = new Map();
for (const item of request.publishAssets ?? []) {
  const name = safeName(item.name, "publish asset name");
  if (assets.has(name)) fail(`duplicate publish asset: ${name}`);
  const file = path.join(assetsRoot, name);
  if (!fs.existsSync(file)) fail(`publish asset is missing: ${item.name}`);
  const actual = record(file);
  if (actual.bytes !== item.bytes || actual.sha256 !== item.sha256) fail(`publish asset digest mismatch: ${name}`);
  assets.set(name, { file, ...actual });
}

const portableManifest = readJson(path.join(requestRoot, "portable-artifact-manifest.json"));
if (portableManifest.sourceCommits?.akuBrowser !== request.sourceCommits.akuBrowser ||
    portableManifest.sourceCommits?.akuBridge !== request.sourceCommits.akuBridge ||
    portableManifest.sourceCommits?.akuSidecar !== request.sourceCommits.akuSidecar ||
    (portableManifest.sourceDirty ?? []).length !== 0) {
  fail("portable artifact provenance does not match the frozen source tuple");
}
if (portableManifest.toolingCommits?.akuBrowser !== request.toolingCommits.akuBrowser ||
    portableManifest.releaseToolingDrift?.releaseSourceSha !== request.sourceCommits.akuBrowser ||
    portableManifest.releaseToolingDrift?.toolingSha !== request.toolingCommits.akuBrowser) {
  fail("portable artifact records the wrong release-tooling boundary");
}

for (const item of request.unsignedManifests ?? []) {
  const inputName = safeName(item.inputName, "unsigned manifest input name");
  const outputName = safeName(item.outputName, "signed manifest output name");
  const artifactNameFromRequest = safeName(item.artifactName, "unsigned manifest artifact name");
  const file = path.join(requestRoot, inputName);
  if (!fs.existsSync(file)) fail(`unsigned manifest is missing: ${inputName}`);
  const actual = record(file);
  if (actual.bytes !== item.bytes || actual.sha256 !== item.sha256) fail(`unsigned manifest digest mismatch
[truncated — 1169 more characters]
```

### scripts/test-macos-preview.sh

```shell
#!/usr/bin/env bash
set -euo pipefail

die() {
  echo "error: $*" >&2
  exit 1
}

require_command() {
  command -v "$1" >/dev/null 2>&1 || die "required command is unavailable: $1"
}

for command_name in node python3 shasum unzip; do
  require_command "$command_name"
done

node_bin="$(command -v node)"
python_bin="$(command -v python3)"
unzip_bin="$(command -v unzip)"

browser_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
release_manifest_path="$browser_root/release/release-manifest.json"
bridge_identity_registry_path="$browser_root/config/bridge-identities.json"
artifact_directory=""
zip_path=""

usage() {
  cat <<'EOF'
Usage: ./scripts/test-macos-preview.sh [options]

Options:
  --artifact-directory <path>  Test an extracted bundle
  --zip <path>                 Extract and test a bundle ZIP
  -h, --help                   Show this help
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --artifact-directory)
      [[ $# -ge 2 ]] || die "--artifact-directory requires a value"
      artifact_directory="$2"
      shift 2
      ;;
    --zip)
      [[ $# -ge 2 ]] || die "--zip requires a value"
      zip_path="$2"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      die "unknown argument: $1"
      ;;
  esac
done

[[ -z "$artifact_directory" || -z "$zip_path" ]] || die "choose either --artifact-directory or --zip"
if [[ -z "$artifact_directory" && -z "$zip_path" ]]; then
  version="$($node_bin --input-type=module -e 'import fs from "node:fs"; console.log(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).version)' "$release_manifest_path")"
  architecture="$(uname -m)"
  case "$architecture" in
    x86_64) architecture="x64" ;;
    arm64|aarch64) architecture="arm64" ;;
    *) die "unsupported macOS host architecture: $(uname -m)" ;;
  esac
  artifact_directory="$browser_root/artifacts/AkuBrowser-${version}-macos-${architecture}"
fi

tmp_root="$(mktemp -d "${TMPDIR:-/tmp}/akubrowser-macos-test.XXXXXX")"
cleanup() {
  if [[ -n "${sidecar_pid:-}" ]] && kill -0 "$sidecar_pid" 2>/dev/null; then
    kill -TERM "$sidecar_pid" 2>/dev/null || true
    wait "$sidecar_pid" 2>/dev/null || true
  fi
  rm -rf -- "$tmp_root"
}
trap cleanup EXIT

if [[ -n "$zip_path" ]]; then
  [[ -f "$zip_path" ]] || die "ZIP does not exist: $zip_path"
  "$unzip_bin" -q "$zip_path" -d "$tmp_root/extracted"
  artifact_directory="$(find "$tmp_root/extracted" -name release-manifest.json -type f -print -quit | sed 's#/release-manifest.json$##')"
fi

artifact_directory="$(cd "$artifact_directory" && pwd)"
[[ -f "$artifact_directory/release-manifest.json" ]] || die "bundle is missing release-manifest.json"
[[ -f "$artifact_directory/artifact-manifest.json" ]] || die "bundle is missing artifact-manifest.json"
[[ -f "$artifact_directory/checksums.sha256" ]] || die "bundle is missing checksums.sha256"
[[ -x "$artifact_directory/AkuSidecar" ]] || die "bundle is missing executable AkuSidecar"
[[ -f "$artifact_directory/AkuBridge/manifest.json" ]] || die "bundle is missing AkuBridge/manifest.json"
[[ -f "$artifact_directory/config/sidecar.json" ]] || die "bundle is missing config/sidecar.json"
[[ -x "$artifact_directory/Start-AkuBrowser.sh" ]] || die "bundle is missing executable Start-AkuBrowser.sh"
[[ -x "$artifact_directory/Start-AkuBrowser.command" ]] || die "bundle is missing executable Start-AkuBrowser.command"
[[ -f "$artifact_directory/README.md" ]] || die "bundle is missing README.md"

while IFS= read -r line; do
  hash="${line%%  *}"
  relative="${line#*  }"
  [[ "$hash" =~ ^[0-9a-f]{64}$ ]] || die "invalid checksum line: $line"
  [[ "$relative" != /* && "$relative" != *".."* ]] || die "unsafe checksum path: $relative"
  file="$artifact_directory/$relative"
  [[ -f "$file" ]] || die "checksummed file is missing: $relative"
  actual="$(shasum -a 256 "$file" | awk '{print $1}')"
  [[ "$actual" == "$hash" ]] || die "checksum mismatch: $relative"
done < "$artifact_directory/checksums.sha256"

target="$($node_bin --input-type=module -e 'import fs from "node:fs"; console.log(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).target)' "$artifact_directory/artifact-manifest.json")"
[[ "$target" == macos-* ]] || die "artifact target is not macOS: $target"

"$node_bin" --input-type=module - "$release_manifest_path" "$bridge_identity_registry_path" "$artifact_directory" <<'NODE'
import fs from "node:fs";

const [releasePath, registryPath, artifactDirectory] = process.argv.slice(2);
const release = JSON.parse(fs.readFileSync(releasePath, "utf8"));
const registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
const artifactRelease = JSON.parse(fs.readFileSync(`${artifactDirectory}/release-manifest.json`, "utf8"));
const artifactManifest = JSON.parse(fs.readFileSync(`${artifactDirectory}/artifact-manifest.json`, "utf8"));
const bridgeManifest = JSON.parse(fs.readFileSync(`${artifactDirectory}/AkuBridge/manifest.json`, "utf8"));
const packageConfig = JSON.parse(fs.readFileSync(`${artifactDirectory}/config/sidecar.json`, "utf8"));
const readme = fs.readFileSync(`${artifactDirectory}/README.md`, "utf8");
const fail = (message) => { throw new Error(message); };

const bridgeIdentityProfile = release.distribution?.offlineBundle?.bridgeIdentityProfile;
const bridgeIdentity = registry.profiles?.[bridgeIdentityProfile];
if (registry.schemaVersion !== 2 || !bridgeIdentityProfile || !bridgeIdentity) {
  fail("the release does not select a valid Bridge identity profile");
}
if (bridgeIdentity.distribution !== "offline-bundle") fail("the release Bridge identity is not an offline profile");
if (!/^[a-p]{32}$/.test(bridgeIdentity.extensionId ?? "")) fail("the release Bridge extension ID is invalid");
const bridgeExtensionOrigin = `chrome-extension://${bridgeIdentity.extensionId}/`;
if (artifactRelease.version !== release.version) fail("artifact release version differs from AkuBrowser");
if (bridgeManifest.version_name !== release.components?.akuBridge?.version) fail("bundled AkuBridge product versio
[truncated — 5075 more characters]
```

### installer/windows/ui_other.go

```go
//go:build !windows

package main

import "fmt"

func finish(_ bool, err error, messages ...string) {
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, message := range messages {
		fmt.Println(message)
	}
}

```

[54 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]