ConnectOnionBack to Docs

ConnectOnion design journal

Engineering notes from the messy middle.

Concrete decisions, failed assumptions, and verified lessons from building Python AI agents, remote browsers, permissions, and release systems.

119

published field notes

Follow via RSS

Latest

Start with what changed recently

Archive

Find the decision behind the code

119 notes

  1. 3 min read

    Gmail kept the body and changed the ID

    A live Gmail test exposed rewritten Message-ID headers. A preserved attempt marker recovers send receipts without risking a duplicate email.

    Remote BrowserReleaseTesting
  2. 3 min read

    The command that blamed the wrong password

    On 27 August, four emails were queued in Outlook for the next morning. An hour later the wording had to change.

    Remote BrowserPermissionsRelease
  3. 4 min read

    The Command That Had to Run on a Broken File

    Draft for 1.8.4; publish after release acceptance.

    Remote BrowserReleaseTesting
  4. 2 min read

    The suite was green and a thread was spinning

    The test named in the timeout was a scroll test. Running it alone did not explain why the complete Linux suite kept stopping there.

    Remote BrowserTestingDeployment
  5. 6 min read

    The request ended. The copy did not.

    The copy test lost its connection just after the NAS returned a task ID. The client had reached its waiting limit. The NAS still had a job to do.

    Remote BrowserReleaseTesting
  6. 2 min read

    The draft that was still number one

    The agent had just listed Gmail drafts. Its next command used a number, as the CLI invited it to do.

    Remote BrowserTesting
  7. 3 min read

    The refresh token we already had

    “Why does YouTube need a database migration?”

    Remote BrowserPermissionsRelease
  8. 2 min read

    A browser on a server, using your address

    Use co proxy share to authorize one remote agent to browse through your computer's internet connection; review latency and network limits.

    Remote BrowserPermissionsRelease
  9. 2 min read

    A reason that nothing read

    Check terminal_reason before browser operations so expired, revoked, or unpaid sessions cannot keep serving pages.

    Remote BrowserTestingRuntime
  10. 2 min read

    Bind first, then prepare

    Acquire the daemon socket lock before preparing shared credentials so a losing startup cannot delete the winner's files.

    Remote BrowserTestingRuntime
  11. 3 min read

    The suffix list, and the table that checked itself

    Audit special-use hostname suffixes and test destination policies against independent fixtures and deliberate mutations.

    Remote BrowserTesting
  12. 3 min read

    The wall was the hard part, not the pipe

    Route a server browser through a home connection while refusing private-network destinations such as 192.168.0.1.

    Remote BrowserReleaseTesting
  13. 4 min read

    Every test was green and the feature had never worked once

    Run native egress preflight through the real browser driver and verify that forbidden destinations actually reach the denying gateway.

    Remote BrowserReleaseTesting
  14. 3 min read

    The check and the browser read different names

    Align URL policy checks with browser parsing and test generator-based deny ranges inside IPv4-mapped IPv6 addresses.

    Remote BrowserReleaseTesting
  15. 4 min read

    The gateway held, but the answer leaked

    Inspect client-facing proxy responses as well as outbound destination checks to expose response-smuggling gaps in egress tests.

    Remote BrowserReleaseTesting
  16. 2 min read

    One Daemon, Many Conversations

    Remove daemon-wide request serialization so a long browser task does not block independent sessions; preserve ordering within each session.

    Remote BrowserTestingRuntime
  17. 2 min read

    127.0.0.1 did not look like an IP address

    Normalize numeric hostnames such as 2130706433 before applying private-address policy so the browser and validator agree.

    Remote BrowserSecurityChromiumSsrf
  18. 3 min read

    A Private Tab Is Not a Private Browser

    Give remote browsing exclusive process, profile, proxy, and socket ownership; owner-bound tabs alone cannot isolate network authority.

    Remote BrowserTestingRuntime
  19. 3 min read

    A Session ID Is Not Browser Authority

    Bind browser operations to the authenticated owner and lifecycle so a session identifier alone cannot grant access after reconnect.

    Remote BrowserTestingRuntime
  20. 3 min read

    A Thread Is Not an Async Runtime

    Replace a shared blocking worker with owned async execution so separate browser sessions can make progress concurrently.

    Remote BrowserReleaseTesting
  21. 2 min read

    Focus Before Delete

    Check that focus reached the intended editor before select-all and delete, preventing successful commands from clearing the wrong content.

    Remote BrowserTestingRuntime
  22. 3 min read

    Input Was the Uncancellable Part

    Move cancellation to the terminal-input boundary so stopping wait_for_manual_login does not leave a worker waiting for input.

    Remote BrowserReleaseTesting
  23. 2 min read

    Sleep Was the Global Lock

    Make humanized typing and pointer timing awaitable so one tab's simulated input does not freeze another tab.

    Remote BrowserReleaseTesting
  24. 3 min read

    The Browser Opened Before We Knew It Was Safe

    Require a browser preflight after launch to prove proxy refusal and network isolation before declaring a private browser ready.

    Remote BrowserPermissionsRelease
  25. 3 min read

    The browser worked until the caller already had a loop

    Keep synchronous Browser methods over one owned event loop, with explicit thread ownership and shutdown joining.

    Remote BrowserReleaseTesting
  26. 3 min read

    The command we had to send before the command

    Check the running daemon's engine before sending commands so a paid Onion browser request cannot silently use another engine.

    Remote BrowserReleaseTesting
  27. 2 min read

    The Matcher Left the Loop

    Move synchronous model-backed element matching off the browser event loop so unrelated tabs respond while a model is pending.

    Remote BrowserReleaseTesting
  28. 2 min read

    The paid launcher was on the wrong loop

    Expose an async Onionwright paid launcher for the Patchright event loop while preserving charge, renewal, expiry, and release ownership.

    Remote BrowserReleaseTesting
  29. 2 min read

    The proxy replied, but the 429 disappeared

    A proxy that closes with unread peer bytes can reset the socket on macOS; test that the client receives the overload response before closure.

    Remote BrowserProxySecurityTesting
  30. 3 min read

    The second tab was not concurrent

    Time simultaneous operations to distinguish tab isolation from concurrency, then move daemon and driver work onto one async runtime.

    Remote BrowserReleaseTesting
  31. 2 min read

    The Upload Button Was in Another Document

    Preserve frame routing and listener timing during async migration so file inputs in another document remain reachable.

    Remote BrowserReleaseTesting
  32. 4 min read

    The URL Passed, but the Socket Was Still Unknown

    Resolve, validate, and dial approved numeric addresses in the gateway; a URL check before page.goto cannot control later connections.

    Remote BrowserReleaseTesting
  33. 6 min read

    A Patch Must Move Forward

    Require a forward-port issue for each stable fix and resolve its applicability to every active newer release before publication.

    ReleaseCI
  34. 2 min read

    A Stable Candidate Cannot Ignore Late Evidence

    Stop stable promotion when unattended browser acceptance reveals a new failure; publish new immutable candidate bytes and repeat the gate.

    Remote BrowserPermissionsRelease
  35. 6 min read

    An Image Prefix Is Not an Image

    Validate complete data URLs and image payloads before upload so base64-looking text cannot enter the image path.

    ReliabilityTesting
  36. 6 min read

    co ai Delegates to Codex and Claude Code — and You Watch It Work

    ConnectOnion 1.7 adds resumable native coding Work Rooms, explicit permission modes, and live tool activity through co ai.

    ReleaseWork Rooms
  37. 6 min read

    Headless Does Not Mean Unconfigured

    Honor explicit command grants in unattended Auto while rejecting broad legacy wildcards that would authorize unlimited actions.

    PermissionsAutomation
  38. 2 min read

    One Fix, Every Newer Line

    The release contract checks Git ancestry and applicable behavioral fixes across supported branches, excluding old version metadata from forward-ports.

    Remote BrowserReleaseTesting
  39. 2 min read

    A Durable Downgrade Must Also Be Live

    Publish the narrowed provider lifecycle revision when Host permissions change so open Work Rooms immediately reflect stored authority.

    Remote BrowserPermissionsRelease
  40. 2 min read

    A new candidate needs a new name

    Use a new candidate version whenever published code changes so install commands, evidence, checksums, and rollback identify the same bytes.

    Remote BrowserReleaseTesting
  41. 2 min read

    A Permission Change Has One Public Truth

    Emit the final permission state after a downgrade so stopping and reopening native provider work cannot expose an intermediate authority.

    Remote BrowserPermissionsRelease
  42. 2 min read

    A permission is a complete state

    Copy both the validated mode and positive turns_left budget into native continuations; missing budgets degrade to Auto without renewing authority.

    Remote BrowserPermissionsRelease
  43. 2 min read

    A tool result is not a final answer

    Distinguish a successful Claude Code tool result from parent-turn completion so an empty model answer cannot strand the chat composer.

    Remote BrowserReleaseTesting
  44. 3 min read

    Arm the deadline before the tool returns

    Arm the parent model's 90-second settlement limit before provider work starts and recover the boundary from durable codex or claude_code traces.

    Remote BrowserPermissionsRelease
  45. 2 min read

    Auto must mean the same promise

    Translate Host Auto intent into each native provider's workspace controls while preserving the separately confirmed Full Access ceiling.

    PermissionsReleaseTesting
  46. 2 min read

    One Approval Boundary Is Enough

    Define one explicit handoff from Host policy to native provider approval so a managed delegation does not stall behind nested dialogs.

    PermissionsReleaseTesting
  47. 2 min read

    The daemon was not the slow part

    Measure the command path before blaming the daemon queue; avoid importing the entire persistent service for every state query.

    Remote BrowserReleaseTesting
  48. 2 min read

    The First Correct Snapshot Was Still Wrong

    Publish one provider snapshot at the final authority ceiling after Stop and downgrade, then verify reopened native sessions.

    Remote BrowserPermissionsRelease
  49. 3 min read

    The last change is the name

    Change package version and Production/Stable metadata together, run twine check, and switch docs only after PyPI serves the final package.

    Remote BrowserPermissionsRelease
  50. 2 min read

    The snapshot that arrived thirty-nine times

    Separate replacement snapshots from append-only events so repeated provider state cannot flood the chat and hide an approval.

    Remote BrowserPermissionsRelease
  51. 2 min read

    The tool finished but the turn did not

    Bound parent model settlement, retry once after a timeout, and end with an explicit error if the retry also expires.

    ReleaseTestingWork Rooms
  52. 2 min read

    The workspace default was already known

    Use the Host-owned workspace when a native coding call omits a directory, while rejecting requests outside the authorized boundary.

    PermissionsReleaseTesting
  53. 3 min read

    A Release Candidate Is a Testable Artifact

    Test a clean public wheel through real browser and provider journeys, and keep its bytes unchanged during release acceptance.

    Remote BrowserPermissionsRelease
  54. 2 min read

    The Work Room Needed Its Own Permissions

    Keep Host authority, provider-session modes, and individual approvals distinct and visible in the native coding client.

    Remote BrowserPermissionsRelease
  55. 6 min read

    What Makes an Agent 10x

    Design agents around verifiable work, durable time, learned experience, bounded delegation, and access to real tools.

    VisionReliability
  56. 2 min read

    A Snapshot Is Not Live State

    Update Control Center readiness from live running and approval events so a stored snapshot cannot label a busy agent available.

    Remote BrowserPermissionsRelease
  57. 2 min read

    A Transcript Is Not a Client

    Send new messages into the same native provider conversation so users can continue work from the remote client.

    Remote BrowserPermissionsRelease
  58. 2 min read

    A Workspace Boundary Is Not a Path

    Reject out-of-workspace provider requests without exposing private roots, missing paths, file names, or symlink targets in public errors.

    Remote BrowserPermissionsRelease
  59. 2 min read

    An Action Is Not a Reason

    Describe the operation being performed above a tool call and leave supporting evidence available without inventing model rationale.

    ReleaseTestingAPI Design
  60. 2 min read

    An Invite Is Not an Environment

    Pass co ai --invite-code for a local run or --invite-code-file for automation while keeping temporary credentials out of persistent configuration.

    Remote BrowserPermissionsRelease
  61. 2 min read

    The Approval Belonged to the Wrong Room

    Place native session decisions in the Work Room that can answer them so Host-level approval does not silently stall provider follow-ups.

    Remote BrowserPermissionsTesting
  62. 2 min read

    The Beta Caught the Wrong Question

    Change tool summaries from inferred reasons to observable actions so type-correct UI contracts do not encourage invented explanations.

    ReleaseTestingAPI Design
  63. 2 min read

    The Bill and the Counter Disagreed

    Use one source of truth for new, cached, and output-token accounting so the chat can explain the charge on a managed request.

    Remote BrowserTestingDeployment
  64. 2 min read

    The Timeout That Blocked Recovery

    Allow recovery actions after DOMContentLoaded times out instead of repeating the same blocked readiness check before every command.

    Remote BrowserReleaseTesting
  65. 2 min read

    The Workroom Lost the Conversation

    Carry provider requests and answers through the shared protocol so live status and tool summaries do not replace the conversation.

    Remote BrowserReleaseWork Rooms
  66. 2 min read

    A Mode Is Not a Compatibility Shim

    Reject legacy authority aliases rather than translating them into Auto, Read only, or Full Access permissions.

    Remote BrowserPermissionsWork Rooms
  67. 2 min read

    A URL Is One Piece of Data

    Render long co auth google URLs without inserted line breaks so headless users can copy the exact credential handoff value.

    Remote BrowserTestingDeployment
  68. 2 min read

    No Dialog Is Not Approval

    Apply the same outside-workspace and unknown-tool policy when no approval UI exists; missing dialogs must not grant authority.

    Remote BrowserPermissions
  69. 2 min read

    The Log Sent Us to the Wrong File

    Pass configuration provenance through deployment layers so invite diagnostics identify the actual source without exposing secrets.

    DeploymentRuntimeAPI Design
  70. 2 min read

    The pipeline that succeeded without doing the work

    Use a nonzero command outcome for unfinished agent work so shell pipelines cannot treat an exhausted iteration budget as success.

    TestingRuntime
  71. 2 min read

    Three Labels Were One Decision

    Map Host Auto, Read only, and Full Access to native provider settings so legacy Default and workspace labels cannot imply different authority.

    Remote BrowserPermissionsRelease
  72. 2 min read

    A Server Quota Is Not a Ceiling

    Expose server region choice from the backend catalog so regional GCE address quotas do not become a global provisioning ceiling.

    DeploymentAPI Design
  73. 3 min read

    Authorise in Advance, So Nobody Signs at 3am

    Separate connection-sharing authorization from tunnel transport and reject attempts by the proxy to authorize its own egress.

    Remote BrowserPermissionsTesting
  74. 2 min read

    New Feature Work Needs a New Number

    Move new remote-browser and server-region work to the next feature line while a frozen release accepts stabilization fixes only.

    Remote BrowserPermissionsRelease
  75. 2 min read

    Sharing a Mailbox Is Not the Same as Giving It Away

    Authorize another agent against an existing mailbox identity so outreach and CRM history stay attached to the original address.

    Remote BrowserPermissionsEmail
  76. 2 min read

    The Browser Gate Was Only Windows

    Run clean-wheel browser acceptance on each supported platform so Windows-only coverage cannot hide macOS daemon failures.

    Remote BrowserPermissionsRelease
  77. 2 min read

    The Model Was Holding the Browser

    Separate model waiting from the browser execution lane so spending-cap retries cannot freeze unrelated browser sessions.

    Remote BrowserReleaseTesting
  78. 6 min read

    Your Servers, Your Regions, Your Shared Mailboxes

    Release notes for server-region choice, shared mailboxes, collision-safe Outlook downloads, and bounded provider requests.

    ReleaseEmail
  79. 2 min read

    A Message Is Not Accepted Until Codex Accepts It

    Keep a follow-up draft until successful steering or native turn start; placing text in a Host queue is not delivery.

    Remote BrowserTestingWork Rooms
  80. 2 min read

    A Stop Request Is Not a Stop

    Distinguish Stop intent, delivery, and terminal outcome so the client reports cancellation only for the targeted provider invocation.

    Remote BrowserTestingWork Rooms
  81. 2 min read

    A Thumbnail Is Evidence, Not a Placeholder

    Render verified workspace or inline PNG evidence and reject outside paths, malformed bytes, and stale revisions.

    Remote BrowserPermissionsRelease
  82. 3 min read

    A Work Room Has One Writer

    Let the provider-owning layer emit lifecycle revisions and retain artifacts while browser consumers reject stale or malformed state.

    Remote BrowserPermissionsRelease
  83. 2 min read

    Auto Is a Boundary, Not a Bypass

    Allow narrow local workspace work without repeated approval while leaving ambiguous, destructive, and broader actions under review.

    Remote BrowserPermissionsRelease
  84. 6 min read

    The Work Room Is a Client, Not a Status Panel

    Keep provider conversation, follow-up input, lifecycle, and controls in one Work Room while the native adapter owns execution.

    Work RoomsOIP
  85. 3 min read

    A Name Is Not an Adapter

    Require a Codex tool card and an openable Work Room from the native provider path; pwd and which codex do not prove a Codex session started.

    Remote BrowserPermissionsRelease
  86. 3 min read

    A Session ID Is Not a Work Room

    Emit provider-owned identity, status, authority, continuation, and replay so a UUID inside a tool result becomes a usable Work Room.

    Remote BrowserPermissionsRelease
  87. 6 min read

    A Tool Transaction Is Not a Work Room

    Send live provider activity and approval events through OIP while keeping raw terminal detail out of the default chat surface.

    Work RoomsOIP
  88. 2 min read

    A User Message Is Not Always a String

    Extract text from structured user content without discarding image parts or calling string methods on a list.

    Remote BrowserRuntimeAPI Design
  89. 6 min read

    A Work Room Is a Summary, Not a Transcript

    Use a finite OIP Work Room summary with verified decisions and evidence instead of replaying raw terminal transcripts by default.

    Work RoomsOIP
  90. 6 min read

    An Approval Is Not Execution Time

    Exclude time spent waiting for an operator's approval from the active provider execution budget.

    PermissionsCodex
  91. 2 min read

    Stop Is a Process-Tree Operation

    Launch shell work in a process group, send SIGTERM on Stop or timeout, then SIGKILL after a grace period; revoke the IO lease to ignore late results.

    Remote BrowserTestingRuntime
  92. 3 min read

    The Long Tail of a Default

    Centralize DEFAULT_MODEL and audit examples, templates, and docs so an old model string does not survive as misleading guidance.

    Remote BrowserReleaseTesting
  93. 3 min read

    The Screenshot Was Not the Test

    Test screenshots alongside provider state, reconnect behavior, and operating-system resources to prove Stop and resume actually work.

    Remote BrowserPermissionsRelease
  94. 4 min read

    The Second Connection Was Still the First

    Preserve relay and session continuity across browser reloads while checking that identity and authority have not changed.

    Remote BrowserPermissionsRelease
  95. 2 min read

    A label is useful only when the work has somewhere to ship

    Classify contributions and require real release metadata so unlabeled work and placeholder milestones cannot disappear into the merge queue.

    Release
  96. 6 min read

    A Page Should Not Become a Wall

    Retrieve later mailbox pages with get_emails(last=1000, offset=2000) or co email inbox --last 1000 --offset 2000.

    EmailAPI Design
  97. 6 min read

    Changing the Default Model Is a Backend Decision First

    Verify backend support before clients request Gemini 3.7, and preserve explicit user model choices.

    ModelsRelease
  98. 3 min read

    Codex Was Ready. Codex Had Never Opened.

    Enforce requested provider identity across the browser protocol and validate the installed wheel so base-model fallback cannot fake a handoff.

    Remote BrowserPermissionsTesting
  99. 2 min read

    Evaluation Is a Choice

    Enable optional judging with co ai --eval; retain the newest 500 generated session records while keeping authored evaluation suites.

    TestingAPI Design
  100. 2 min read

    Every PR tells its story

    Require a docs/blog post through the blog-gate CI check so implemented capabilities ship with CLI help, user documentation, and engineering lessons.

    TestingAPI Design
  101. 6 min read

    One Browser Protocol, Native Coding Adapters

    Use one browser-facing protocol while native backend adapters retain provider sessions, approval behavior, and execution ownership.

    OIPArchitecture
  102. 4 min read

    One Default, Five Answers

    Replace repeated model strings with one DEFAULT_MODEL while preserving explicit user configuration and a documented rollback.

    Remote BrowserReleaseTesting
  103. 2 min read

    Ten minutes was not a timeout policy

    Stop silently capping caller timeouts at ten minutes and propagate real timeout errors through direct APIs and Agent traces.

    TestingRuntimeAPI Design
  104. 6 min read

    The Agent That Was Itself, and Billed Someone Else

    Control which credentials a deployed process inherits so operator configuration cannot silently replace agent identity or billing authority.

    SecurityDeployment
  105. 2 min read

    The cache was invisible

    Surface provider cached-token counts in local accounting and terminal output so repeated requests show their actual charge.

    Remote BrowserReleaseTesting
  106. 4 min read

    The CLI is the product

    Expose API features in CLI help and route email ownership failures to co email addresses so users can find a valid next command.

    ReleaseTestingEmail
  107. 6 min read

    The Owner Needs a Door

    Provide a private owner recovery path that permits initial agent access without leaking invite credentials into logs.

    SecurityOnboarding
  108. 2 min read

    The Third Argument Was Already Taken

    Preserve the reply method's existing positional arguments while adding attachment validation and regression tests for legacy callers.

    Remote BrowserReleaseTesting
  109. 2 min read

    When “Download” Quietly Meant “Replace”

    Allocate unique download paths for duplicate attachment names and existing local files, then return the paths actually written.

    Remote BrowserReleaseTesting
  110. 2 min read

    When logging looked like authoring

    Distinguish source authoring from command-output logging so append redirects and tee can write audit records without retry loops.

    Remote BrowserTestingRuntime
  111. 6 min read

    ConnectOnion 1.6.0

    Review the release's remote-agent controls and the separation of deployed agent credentials from operator identity.

    ReleaseSecurity
  112. 6 min read

    Auto-Debug Design Evolution

    Compare five debugging designs that reduced mode complexity and focused the workflow on inspecting live agent execution.

    DebuggingUX
  113. 6 min read

    Progressive Disclosure in CLI Design

    Scaffold a working agent project first, then reveal configuration choices when users need them instead of requiring a setup questionnaire.

    CLIUX
  114. 6 min read

    Why “Address” Beats “Identity”

    Use address for network routing and keep reputation, authorization, and broader identity concepts separate in the public API.

    API DesignNaming
  115. 6 min read

    Why Agent Addresses Use Hex-Encoded Ed25519 Keys

    Represent the full Ed25519 public key in hexadecimal with a 0x prefix so an address directly carries the agent's public verification key.

    SecurityNetwork
  116. 6 min read

    Why We Chose input() Over run()

    Send a task through Agent.input() so the primary method names the caller's input rather than exposing a generic execution verb.

    API DesignUX
  117. 6 min read

    Why We Chose llm_do() Over llm()

    Use llm_do() for a single LLM task without creating an Agent; the function name states an action rather than naming a model object.

    API DesignNaming
  118. 6 min read

    Why We Chose “Trust”

    Use trust as the public API concept for both accepting incoming agents and deciding which remote agents may be called.

    API DesignSecurity
  119. 6 min read

    Designing the ConnectOnion Network Protocol

    Compare agent addressing, messages, and direct connections with the earlier identity and session machinery.

    ArchitectureNetwork

Star us on GitHub

If ConnectOnion saves you time, a ⭐ goes a long way — and earns you a coffee chat with our founder.