Skip to content

bug: firefox error with cin permissions #53

Closed

Description

@kbuffardi

After loading the firefox plugin xpi and launching the extension, it produces the following error in the terminal:

browser.cpp:~$ Interactive stdin requires SharedArrayBuffer, which is unavailable in this context.  The page must be served with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers.

Activity

  1. kbuffardi commented on Jul 31, 2026

    @kbuffardi
    OwnerAuthor

    Implementation plan — Firefox cin / SharedArrayBuffer fallback

    Planned by Codex plan-agent (via GPT-5.5) in response to the human request: “plan-agent for issue #53”. No implementation changes have been made.

    Root cause

    This is not a normal extension permission failure. src/ui/terminal.js currently blocks every run when SharedArrayBuffer is unavailable and prints the COOP/COEP error. Chromium receives cross-origin isolation through the extension manifest, but the generated Firefox package inherits manifest keys that Firefox extension pages do not currently use to make crossOriginIsolated true. The synchronous WASI fd_read implementation in src/workers/wasi-shim.mjs uses Atomics.wait on shared memory, so live terminal-to-worker input cannot simply be replaced with an asynchronous postMessage without redesigning the execution model.

    Recommended design

    Keep the current live interactive stdin path when SharedArrayBuffer/cross-origin isolation is available. In non-SAB contexts such as the Firefox extension, allow programs to run using stdin text collected before execution, consume those bytes synchronously in WASI fd_read, then return EOF. Present this as a Firefox limitation rather than instructing extension users to configure HTTP headers.

    A manifest/header-only fix is not recommended because Firefox extension pages do not currently expose the same cross-origin-isolation behavior as Chromium. A hosted isolated page plus extension bridge would be substantially larger and conflict with the local/offline architecture.

    Ordered tasks

    1. Add buffered stdin tests in scripts/e2e-wasi-shim.test.mjs.

      • Verify preloaded bytes drain across repeated reads and iovecs in order.
      • Verify subsequent reads return EOF.
      • Preserve existing SAB-path coverage.
    2. Extend the WASI input modes in src/workers/wasi-shim.mjs.

      • Accept either the existing shared buffer or buffered stdin bytes.
      • Keep Atomics.wait behavior unchanged for interactive mode.
      • Make buffered/no-input modes safe and deterministic; no input means immediate EOF.
    3. Extend the worker run contract in src/workers/compiler.worker.js.

      • Preserve { sharedBuffer } for interactive mode.
      • Add a serialized stdin-buffer field for fallback mode.
      • Preserve VFS, binary, output, and writeback behavior.
    4. Add the UI capability gate and buffered-input flow in src/ui/terminal.js and src/ui/app.js (plus minimal styling only if required).

      • Stop hard-failing when SAB is unavailable.
      • Keep Chromium interactive stdin unchanged.
      • Collect stdin before a Firefox/non-SAB run and pass encoded bytes to the worker.
      • Ensure toolbar Compile & Run follows the same path.
      • Replace generic server-header guidance with accurate extension-context messaging.
    5. Update compatibility reporting and tests in src/ui/browser-capabilities.mjs, scripts/e2e-browser-compatibility.test.mjs, and scripts/e2e-firefox-compatibility.test.mjs.

      • Report Firefox without SAB as supported with limited stdin, not as a total run failure.
      • Distinguish “live interactive stdin unavailable” from “compile/run unavailable.”
    6. Clean Firefox manifest expectations in scripts/build-target-manifest.js and Firefox compatibility tests.

      • Prefer stripping unsupported Chromium-only cross-origin policy keys from the generated Firefox manifest so web-ext lint does not report them.
      • Document why the Chrome manifest retains them and Firefox does not.
    7. Add Firefox runtime validation through the smallest feasible automated smoke coverage; otherwise document an explicit manual XPI check.

      • Install the XPI, compile a cin program, provide buffered input, confirm expected stdout, and confirm the SAB/header hard error is absent.
      • Preserve Chromium browser smoke coverage.

    Checkpoints and verification

    After the runtime/worker work, run focused WASI and compatibility E2E tests. After the UI capability work, manually confirm live stdin still works in Chrome and buffered cin works in the Firefox XPI.

    Before PR review:

    npm run lint
    npm run build
    npm run test:e2e
    npm run test:browser:firefox
    npm run test:browser:chrome

    If toolchain artifacts are absent, run npm run fetch-clang and npm run test:preflight-clang first.

    Risks / review decisions

    • True live interactive stdin remains unavailable in Firefox under the current synchronous WASI/SAB architecture; this plan restores cin through buffered input.
    • The buffered-input UX should remain minimal and must not affect Chromium’s terminal flow.
    • startRun() may need to become asynchronous; callers in src/ui/toolbar.js must tolerate that.
    • Recommend stripping Firefox’s unsupported isolation keys now, while retaining automatic SAB preference so future Firefox support can enable the interactive path without another architecture change.

    Delivery

    Use branch fix/firefox-stdin-fallback. Open a pull request that includes Closes #53; a human reviews and merges it.

  2. kbuffardi commented on Jul 31, 2026

    @kbuffardi
    OwnerAuthor

    Plan-reviewer critique — required revisions

    Human request from @kbuffardi (verbatim): “plan-reviewer critique this”

    Reviewed by Codex plan-reviewer (via GPT-5.5) against the issue plan, current source, tests, scripts, and package commands.

    Decision: APPROVE WITH REQUIRED REVISIONS. The buffered-stdin fallback is a defensible Firefox solution, but the current plan is not yet precise enough to implement safely.

    Blocking findings

    1. Define the asynchronous run lifecycle. terminal.startRun() is synchronous today, while workspace collection and worker dispatch are asynchronous; toolbar callers do not await it. The plan must define a contract such as startRun(): Promise<boolean>, including cancel/error results, duplicate clicks during stdin collection, and restoration of controls when no worker run-start occurs.

    2. Define a discriminated worker/WASI contract. Replace the vague optional fields with explicit modes, for example { stdinMode: 'interactive', sharedBuffer } and { stdinMode: 'buffered', stdinBuffer }. Specify accepted binary types, validation, deterministic EOF, empty/no-input behavior, and invalid-message reporting.

    3. Change the Firefox capability model, not only its copy. Current core capabilities require SAB/Atomics.waitAsync, and the Firefox test fixture fakes SAB plus isolation. Add a Firefox/SAB=false case where compile/run remains supported (report.ok === true) while live interactive stdin is reported as unavailable.

    4. Make Firefox manifest cleanup mandatory. Strip cross_origin_opener_policy and cross_origin_embedder_policy from generated Firefox manifests and assert both are absent. Retain them for Chromium.

    5. Require validation that reproduces bug: firefox error with cin permissions #53. npm run test:browser:firefox currently checks manifest shape, web-ext lint, and packaging—not runtime compile/run. Add runtime automation or a concrete manual XPI acceptance script with an exact cin program, exact supplied input, expected stdout, and an assertion that the old SAB/header error is absent.

    Important improvements

    • Describe the feature as pre-supplied/buffered stdin, not fully interactive cin; program prompts may only appear after input was collected.
    • Test empty input, no trailing newline, repeated reads/multiple iovecs, long input, UTF-8 bytes, cancellation, stopping, and worker validation errors.
    • Use a cursor over Uint8Array, not repeated Array.shift().
    • Require an accessible input UI: labelled textarea, focus management, keyboard cancellation, safe text handling, and a practical size limit.
    • Update README.md and docs/release-playbook.md, which currently imply fully interactive/full-parity stdin.

    Revised ordered plan

    1. Record the reviewed limitation: Chromium keeps live SAB stdin; Firefox gets pre-supplied stdin until extension-page isolation is supported.
    2. Define and test the discriminated run input contract and EOF/error semantics.
    3. Add focused WASI buffered-input tests, retaining SAB regressions.
    4. Implement the buffered input source without constructing SAB views in buffered mode.
    5. Validate and route both modes in compiler.worker.js, preserving VFS/writeback behavior.
    6. Refactor terminal/toolbar around an async preparing-run state with cancellation and error recovery.
    7. Update capability reporting/tests for Firefox with SAB absent but limited compile/run support.
    8. Strip Firefox COOP/COEP manifest keys and assert their absence.
    9. Update compatibility and release documentation.
    10. Run lint, build, E2E, Chrome smoke, Firefox package smoke, and an automated or explicit manual Firefox XPI runtime acceptance check.

    Use fix/firefox-stdin-fallback; the eventual PR must include Closes #53. Post the final revised plan to this issue before implementation.

  3. kbuffardi commented on Jul 31, 2026

    @kbuffardi
    OwnerAuthor

    Revised implementation plan — supersedes the original plan

    Human request from @kbuffardi (verbatim): “revise with required changes”

    Revised by Codex plan-agent (via GPT-5.5) in response to the required revisions in the plan-reviewer critique. No implementation has begun.

    Outcome and architecture

    Issue #53 will be fixed with two explicit stdin modes:

    • Chromium: retain live interactive stdin through SharedArrayBuffer and Atomics.
    • Firefox/non-SAB contexts: collect pre-supplied stdin before execution, pass UTF-8 bytes to the worker, consume them synchronously through WASI fd_read, then return deterministic EOF.

    Firefox buffered stdin is not a live terminal. Program prompts may appear only after the input has already been supplied.

    Implementation branch: fix/firefox-stdin-fallback. The eventual PR must include Closes #53. A human must approve this plan before implementation begins.

    Exact run contract

    Worker run requests must use one of these discriminated variants while preserving existing vfsFiles and binaryBytes fields:

    { type: 'run', stdinMode: 'interactive', sharedBuffer: SharedArrayBuffer, ... }
    { type: 'run', stdinMode: 'buffered', stdinBuffer: Uint8Array | ArrayBuffer, ... }
    { type: 'run', stdinMode: 'none', ... }

    Rules:

    • interactive requires an actual SharedArrayBuffer and preserves the current SAB/Atomics implementation.
    • buffered accepts only Uint8Array or ArrayBuffer, converts once to Uint8Array, and reads with a cursor plus subarray; it must not use Array.shift() or construct SAB views.
    • none returns successful EOF (nread = 0) immediately.
    • Buffered reads span repeated calls and multiple iovecs in order; once exhausted, every later read returns successful EOF.
    • binaryBytes and vfsFiles[].bytes accept only Uint8Array or ArrayBuffer.
    • Invalid requests emit no run-start; they emit a clear stderr validation message and run-result with exitCode: 1, empty vfsChanges, and empty vfsDeletes.

    Exact UI lifecycle

    • terminal.startRun(): Promise<boolean>.
    • true means a valid run request was prepared and posted; false means none was posted because there is no binary, a run is already preparing/running, the user canceled, validation failed, or the callback rejected.
    • preparingRun covers buffered-input collection and asynchronous VFS preparation.
    • Duplicate Run or Compile & Run actions while preparing/running do nothing and post no additional worker messages.
    • run-start is the only transition into actual running state; it clears preparingRun, enables Stop, and activates terminal stdin key handling only for interactive mode.
    • Cancel/error before run-start restores prompt, focus, toolbar, and terminal controls without waiting for run-result.
    • Direct Run and Compile & Run callers must await startRun(); compile-after-run state must not remain stuck after cancellation or errors.
    • Stop remains idempotent while running and does not corrupt cancellation during preparation.

    The buffered-input UI must provide a labelled textarea, Run and Cancel controls, byte-count/limit feedback, focus entry/return, and Escape cancellation. It must use form values/textContent, never innerHTML. Maximum encoded input is 256 KiB; over-limit input remains editable, announces the error accessibly, and posts nothing.

    Ordered tasks

    1. Specify the async run lifecycle with failing tests — M

    Likely files: scripts/e2e-terminal-stop.test.mjs, src/ui/terminal.js.

    Acceptance:

    • startRun() resolves false for missing binary, already preparing/running, cancellation, and callback rejection.
    • Duplicate actions during input collection post no additional run request.
    • Cancel/error restores idle prompt, focus, and controls without run-start.

    Verification: node --experimental-detect-module --test scripts/e2e-terminal-stop.test.mjs

    Dependencies: none.

    2. Add buffered-input UI/accessibility tests — M

    Likely files: new scripts/e2e-buffered-stdin-ui.test.mjs, src/ui/index.html, src/ui/styles.css, src/ui/terminal.js.

    Acceptance:

    • Labelled textarea, Run/Cancel, byte feedback, Escape cancellation, and focus restoration are covered.
    • Submitted text is handled only as form text; no HTML injection path exists.
    • Empty input is valid; over-256-KiB UTF-8 input is rejected without worker dispatch.

    Verification: node --experimental-detect-module --test scripts/e2e-buffered-stdin-ui.test.mjs

    Dependencies: Task 1.

    3. Add buffered WASI stdin tests — M

    Likely file: scripts/e2e-wasi-shim.test.mjs.

    Acceptance:

    • Bytes drain in order across repeated fd_read calls and multiple iovecs.
    • Cover empty input, no trailing newline, UTF-8, long input below the cap, and EOF after exhaustion.
    • Existing SAB interactive behavior remains covered.

    Verification: node --experimental-detect-module --test scripts/e2e-wasi-shim.test.mjs

    Dependencies: none.

    4. Implement WASI stdin source modes — M

    Likely files: src/workers/wasi-shim.mjs, scripts/e2e-wasi-shim.test.mjs.

    Acceptance:

    • Interactive mode preserves SAB behavior.
    • Buffered mode uses Uint8Array plus a cursor and never constructs shared views.
    • None mode returns immediate EOF.

    Verification: focused WASI test command from Task 3.

    Dependencies: Task 3.

    5. Validate and route worker run requests — M

    Likely files: src/workers/compiler.worker.js and a focused worker-contract test or scripts/e2e-wasi-shim.test.mjs.

    Acceptance:

    • All three valid modes reach the matching WASI source.
    • Invalid modes/byte fields emit validation stderr and failed run-result, never run-start.
    • Existing VFS, stdout/stderr, and writeback behavior remains unchanged.

    Verification: focused worker/WASI tests.

    Dependencies: Task 4.

    6. Implement async terminal and toolbar routing — M

    Likely files: src/ui/terminal.js, src/ui/app.js, src/ui/toolbar.js, scripts/e2e-terminal-stop.test.mjs.

    Acceptance:

    • Direct Run and Compile & Run await startRun().
    • Compile & Run state clears correctly on success, cancellation, and failure.
    • run-start controls actual running state; Stop remains idempotent.

    Verification: focused terminal lifecycle tests.

    Dependencies: Tasks 1, 4, and 5.

    7. Implement accessible buffered stdin collection — M

    Likely files: src/ui/terminal.js, src/ui/index.html, src/ui/styles.css, scripts/e2e-buffered-stdin-ui.test.mjs.

    Acceptance:

    • Firefox/non-SAB Run collects and UTF-8 encodes stdin before dispatch.
    • Cancel/Escape resolve false, restore focus/state, and post nothing.
    • The 256-KiB encoded-byte cap is enforced accessibly.

    Verification: buffered UI and terminal lifecycle tests.

    Dependencies: Tasks 1, 2, and 6.

    8. Separate compile/run support from live stdin capability — M

    Likely files: src/ui/browser-capabilities.mjs, scripts/e2e-browser-compatibility.test.mjs, scripts/e2e-firefox-compatibility.test.mjs.

    Acceptance:

    • Firefox fixture with no SAB, crossOriginIsolated: false, and no Atomics.waitAsync has report.ok === true.
    • The report marks live interactive stdin unavailable and exposes buffered/pre-supplied stdin as a limitation.
    • Chromium retains its full-parity SAB requirements.

    Verification: node --experimental-detect-module --test scripts/e2e-browser-compatibility.test.mjs scripts/e2e-firefox-compatibility.test.mjs

    Dependencies: Task 7.

    9. Strip Firefox COOP/COEP manifest keys — S

    Likely files: scripts/build-target-manifest.js, scripts/e2e-firefox-compatibility.test.mjs, scripts/smoke-firefox.mjs.

    Acceptance:

    • createFirefoxManifest() deletes cross_origin_opener_policy and cross_origin_embedder_policy.
    • Firefox generated-manifest and smoke tests assert both keys are absent.
    • Chromium output retains both base-manifest keys.

    Verification:

    node --experimental-detect-module --test scripts/e2e-firefox-compatibility.test.mjs
    npm run build
    npm run test:browser:firefox

    Dependencies: none.

    10. Update browser support and release documentation — S

    Likely files: README.md, docs/release-playbook.md, new docs/firefox-stdin-runtime-acceptance.md.

    Acceptance:

    • README distinguishes Chromium live stdin from Firefox pre-supplied stdin.
    • Release QA replaces generic Firefox “terminal interaction” language with the exact acceptance below.
    • Documentation records the limitation and the 256-KiB cap.

    Verification: targeted rg review plus normal lint/build.

    Dependencies: Tasks 7–9.

    11. Prove issue #53 in a Firefox runtime — M

    Add reliable Firefox extension runtime automation if feasible; otherwise the exact manual XPI/unpacked-extension procedure in docs/firefox-stdin-runtime-acceptance.md is mandatory and its result must be pasted into the PR.

    Use exactly:

    #include <iostream>
    #include <string>
    
    int main() {
        std::string name;
        int age = 0;
    
        std::cout << "Name? ";
        std::cin >> name;
        std::cout << "Age? ";
        std::cin >> age;
    
        if (!std::cin) {
            std::cerr << "input failed\n";
            return 2;
        }
    
        std::cout << "\nHello " << name << ", next year " << (age + 1) << "\n";
        return 0;
    }

    Buffered input:

    Ada
    41
    

    Expected stdout:

    Name? Age? 
    Hello Ada, next year 42
    

    Acceptance:

    • Exit code is 0 and stderr is empty.
    • Output matches the expected stdout.
    • Output does not contain Interactive stdin requires SharedArrayBuffer, Cross-Origin-Opener-Policy, or Cross-Origin-Embedder-Policy.

    Verification: npm run build, npm run test:browser:firefox, plus the documented runtime procedure unless replaced by reliable automation.

    Dependencies: Tasks 7–10.

    12. Full validation and PR handoff — S

    Acceptance:

    • npm run lint, npm run build, npm run test:e2e, npm run test:browser:firefox, and npm run test:browser:chrome pass.
    • Runtime acceptance records browser version, artifact path, stdout/stderr, and absence of the old error.
    • PR uses branch fix/firefox-stdin-fallback, includes Closes #53, and is left for human review/merge.

    Dependencies: Tasks 1–11.

    Checkpoints

    • After Tasks 1–5: focused WASI, worker-contract, and terminal lifecycle tests pass.
    • After Tasks 6–8: buffered UI, terminal, and capability tests pass; Chrome live stdin remains unchanged.
    • After Task 9: build, Firefox manifest tests, and Firefox package smoke pass.
    • After Tasks 10–11: exact Firefox runtime acceptance passes.
    • Before PR: run the full validation suite and record results.

    Risks and mitigations

    • Users may mistake buffered input for live prompts: label it as pre-supplied stdin everywhere.
    • Async preparation may strand controls: startRun(false) restores state, while only run-start enters running.
    • Invalid worker messages may desynchronize UI: failed run-result must be tolerated without an active run.
    • Large input could perform poorly: enforce 256 KiB and use cursor/subarray copies.
    • Firefox may re-inherit Chromium keys later: assert their absence in both E2E and smoke tests.
    • Firefox runtime automation may remain impractical: the exact manual acceptance is mandatory and PR-recorded.

    Required validation

    npm run lint
    npm run build
    npm run test:e2e
    npm run test:browser:firefox
    npm run test:browser:chrome

    If Clang artifacts are absent:

    npm run fetch-clang
    npm run test:preflight-clang

    Reviewer finding resolution

    • Async lifecycle: resolved with Promise<boolean>, preparingRun, caller awaiting, cancellation/error recovery, and run-start as the running boundary.
    • Worker/WASI contract: resolved with explicit variants, accepted types, validation failure behavior, EOF/no-input semantics, and cursor-based reads.
    • Firefox capabilities: resolved with a real SAB=false fixture that remains compile/run capable but reports limited stdin.
    • Manifest cleanup: resolved by mandatory deletion and assertions for both Firefox keys.
    • Runtime proof: resolved with an exact program, input, output, empty-stderr requirement, and explicit absence of the original error.
  4. kbuffardi commented on Jul 31, 2026

    @kbuffardi
    OwnerAuthor

    Implementation started

    Human request from @kbuffardi (verbatim): “implement this plan and raise a pr”

    Codex (GPT-5) has begun the approved revised plan on branch fix/firefox-stdin-fallback. Work will follow test-first increments, preserve Chromium live interactive stdin, add Firefox pre-supplied buffered stdin, run the documented validation suite, and open a draft PR with Closes #53. The unrelated untracked planning files in the local checkout are explicitly out of scope.

  5. kbuffardi commented on Jul 31, 2026

    @kbuffardi
    OwnerAuthor

    Implementation is ready for review in draft PR #54: #54

    Automated validation is green (test:e2e: 163 passed, lint, build, and Firefox packaging smoke). The PR explicitly tracks the remaining hands-on Firefox runtime acceptance procedure and the Chrome smoke harness timeout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions