| # Orchestration |
|
|
| This page describes the current orchestration layer in PinchTab: how the server launches, tracks, routes to, and stops browser instances. |
|
|
| ## Scope |
|
|
| The orchestrator is part of server mode. It is responsible for: |
|
|
| - launching managed instances as child `pinchtab bridge` processes |
| - attaching externally managed Chrome instances when attach policy allows it |
| - tracking instance status and metadata |
| - routing tab-scoped requests to the owning managed instance |
| - stopping managed instances and cleaning up registry state |
|
|
| It does not execute browser actions directly. That work happens inside the bridge runtime. |
|
|
| ## Current Runtime Shape |
|
|
| ```mermaid |
| flowchart TD |
| S["PinchTab Server"] --> O["Orchestrator"] |
| |
| O --> M1["Managed Instance"] |
| O --> M2["Managed Instance"] |
| O -.->|attach| A1["Attached External Instance"] |
| |
| M1 --> B1["pinchtab bridge child"] |
| M2 --> B2["pinchtab bridge child"] |
| |
| B1 --> C1["Chrome"] |
| B2 --> C2["Chrome"] |
| ``` |
|
|
| ## Launch Flow |
|
|
| For a managed instance, the orchestration flow is: |
|
|
| ```mermaid |
| flowchart LR |
| R["Start Request"] --> V["Validate profile + port"] |
| V --> W["Write child config"] |
| W --> P["Spawn pinchtab bridge"] |
| P --> H["Poll /health on loopback addresses"] |
| H --> S{"Healthy before timeout?"} |
| S -->|Yes| OK["Mark running"] |
| S -->|No| ER["Mark error"] |
| ``` |
|
|
| What the code does today: |
|
|
| - validates the profile name before launch |
| - allocates a port when one is not supplied |
| - prevents more than one active managed instance per profile |
| - prevents reusing a port already in use |
| - writes a child config file under the profile state directory |
| - launches `pinchtab bridge` |
| - polls `/health` on `127.0.0.1`, `::1`, and `localhost` |
| - moves the instance from `starting` to `running` or `error` |
|
|
| ## Attach Flow |
|
|
| Attach is a separate path for an already running browser. |
|
|
| ```mermaid |
| flowchart LR |
| R["POST /instances/attach"] --> P["Validate attach policy"] |
| P --> A["Register external instance"] |
| A --> L["Add to instance registry"] |
| ``` |
|
|
| Current attach behavior: |
|
|
| - requires `security.attach.enabled` |
| - validates the CDP URL against `security.attach.allowSchemes` |
| - validates the host against `security.attach.allowHosts` |
| - registers the instance as `attached: true` |
| - does not start or stop an external Chrome process |
|
|
| ## Routing Model |
|
|
| The orchestrator is also the routing layer for multi-instance server mode. |
|
|
| ```mermaid |
| flowchart LR |
| R["Tab-scoped request"] --> T["Resolve tab owner"] |
| T --> C["Locator cache"] |
| C -->|hit| P["Proxy to instance port"] |
| C -->|miss| F["Fetch /tabs from running instances"] |
| F --> P |
| ``` |
|
|
| Today, tab routing works like this: |
|
|
| - for routes such as `/tabs/{id}/navigate` and `/tabs/{id}/action`, the server resolves which instance owns the tab |
| - it first tries the instance locator cache |
| - on cache miss, it falls back to scanning running instances through `/tabs` |
| - after resolution, it proxies the request to the owning bridge instance |
|
|
| This keeps the public server API stable while the bridge instances remain isolated. |
|
|
| Important limitation: |
|
|
| - this routing path is fully shaped around managed bridge-backed instances that expose loopback HTTP ports |
| - attached instances are registered and surfaced in the instance registry, but the normal tab-owner proxy path is not yet equally first-class for them |
|
|
| ## Stop Flow |
|
|
| Stopping a managed instance is a server-owned lifecycle operation. |
|
|
| ```mermaid |
| flowchart LR |
| R["Stop Request"] --> S["Mark stopping"] |
| S --> G["POST /shutdown to instance"] |
| G --> W{"Exited?"} |
| W -->|No| T["SIGTERM"] |
| T --> K{"Exited?"} |
| K -->|No| X["SIGKILL"] |
| W -->|Yes| D["Remove from registry"] |
| K -->|Yes| D |
| X --> D |
| ``` |
|
|
| Current stop behavior: |
|
|
| - marks the instance as `stopping` |
| - tries graceful shutdown through the instance HTTP API |
| - falls back to process-group termination when needed |
| - releases the allocated port |
| - removes the instance from the registry and locator cache |
|
|
| For attached instances, there is no child process to kill; the orchestrator only removes its own registration state. |
|
|
| ## Instance States |
|
|
| The main statuses surfaced today are: |
|
|
| - `starting` |
| - `running` |
| - `stopping` |
| - `stopped` |
| - `error` |
|
|
| The orchestrator also emits lifecycle events such as: |
|
|
| - `instance.started` |
| - `instance.stopped` |
| - `instance.error` |
| - `instance.attached` |
|
|
| ## Relationship To Other Layers |
|
|
| - **Strategy layer** decides how shorthand requests are exposed or routed in server mode |
| - **Scheduler** is optional and sits above the same routed execution path |
| - **Bridge** owns browser state, tab state, and CDP execution |
| - **Profiles** provide persistent browser data on disk |
|
|