WitNote / docs /core-concepts.md
AUXteam's picture
Upload folder using huggingface_hub
6a7089a verified

Core Concepts

This document describes the concepts that are implemented today in PinchTab.

Server

The server is the main PinchTab process.

Start it with:

pinchtab
# or explicitly
pinchtab server

What the server does:

  • exposes the main HTTP API and dashboard on port 9867 by default
  • manages profiles and instances
  • proxies tab-scoped requests to the correct managed instance
  • can expose shorthand routes such as /navigate, /snapshot, and /action

Important clarification:

  • the server is the public entry point
  • for managed instances, the server usually does not talk to Chrome directly
  • instead, it spawns or routes to a per-instance bridge process

Bridge

The bridge is the single-instance runtime.

Start it directly only when you want one standalone browser runtime:

pinchtab bridge

What the bridge does:

  • owns exactly one Chrome browser process
  • exposes browser and tab endpoints such as /navigate, /snapshot, /action, and /tabs/{id}/...
  • is the process the server launches for each managed instance

In normal multi-instance usage, you usually interact with the server, not with bridge processes directly.

Profiles

A profile is a Chrome user data directory.

It stores persistent browser state such as:

  • cookies
  • local storage
  • cache
  • browsing history
  • extensions
  • saved account state

Profile facts that match the current implementation:

  • profiles are persistent on disk
  • profiles can exist without any running instance
  • at most one active managed instance can use a given profile at a time
  • profile IDs use the format prof_XXXXXXXX
  • GET /profiles hides temporary auto-generated profiles unless you pass ?all=true

Create a profile with the API:

curl -X POST http://localhost:9867/profiles \
  -H "Content-Type: application/json" \
  -d '{
    "name": "work",
    "description": "Main logged-in work profile"
  }'
# Response
{
  "status": "created",
  "id": "prof_278be873",
  "name": "work"
}

Instances

An instance is a managed browser runtime.

In practice, one instance means:

  • one bridge process
  • one Chrome process
  • zero or one profile
  • one dedicated port
  • many tabs

Instance facts that match the current implementation:

  • instance IDs use the format inst_XXXXXXXX
  • ports are auto-allocated from 9868-9968 by default
  • instance status is tracked as starting, running, stopping, stopped, or error
  • one profile cannot be attached to multiple active managed instances at the same time

Persistent vs temporary instances

There are two common ways to start an instance:

  1. with a named profile
  2. without a profile ID

If you start an instance with a profile ID, the instance uses that persistent profile.

If you start an instance without a profile ID, PinchTab creates an auto-generated profile named like instance-.... That temporary profile is deleted when the instance stops.

So this is the correct mental model:

  • instances without an explicit profile are ephemeral
  • the implementation still creates a temporary profile directory behind the scenes
  • that temporary profile is cleanup state, not a reusable long-term profile

Starting an instance

Preferred endpoint:

curl -X POST http://localhost:9867/instances/start \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "prof_278be873",
    "mode": "headed"
  }'
# CLI Alternative
pinchtab instance start --profileId prof_278be873 --mode headed
# Response
{
  "id": "inst_0a89a5bb",
  "profileId": "prof_278be873",
  "profileName": "work",
  "port": "9868",
  "headless": false,
  "status": "starting"
}

Tabs

A tab is a single page inside an instance.

Tabs belong to an instance, and therefore inherit that instance's profile state.

What a tab gives you:

  • its own URL and page state
  • a snapshot of the accessibility tree
  • action execution such as click, type, fill, hover, and press
  • text extraction, screenshots, PDF export, cookie access, and evaluation

Open a tab in a specific instance:

INST=inst_0a89a5bb

curl -X POST http://localhost:9867/instances/$INST/tabs/open \
  -H "Content-Type: application/json" \
  -d '{"url":"https://pinchtab.com"}'
# Response
{
  "tabId": "CDP_TARGET_ID"
}

Then use tab-scoped endpoints:

TAB=CDP_TARGET_ID

curl http://localhost:9867/tabs/$TAB/snapshot

curl -X POST http://localhost:9867/tabs/$TAB/action \
  -H "Content-Type: application/json" \
  -d '{"kind":"click","ref":"e5"}'

curl -X POST http://localhost:9867/tabs/$TAB/close

Are tabs persistent?

Usually, no.

For managed instances started by the server:

  • tabs are runtime objects
  • tabs disappear when the instance stops
  • profiles persist, but open tabs do not

That means the persistent part is the profile state, not the tab list.

Element references

Snapshots return element references such as e0, e1, e2, and so on.

These refs are useful because they let you interact with elements without writing CSS selectors for common flows.

Relationships

The implementation is easiest to understand with these rules:

Relationship What is true today
Server -> Instances One server can manage many instances
Bridge -> Chrome One bridge owns one Chrome process
Instance -> Profile An instance has zero or one profile
Profile -> Instance A profile can have zero or one active managed instance at a time
Instance -> Tabs An instance can have many tabs
Tab -> Instance Every tab belongs to exactly one instance
Tab -> Profile A tab inherits the instance profile, if one exists

Profiles are reusable persistent state. Instances are temporary runtimes that may use a profile.

Shorthand routes vs explicit routes

PinchTab exposes two styles of interaction:

Explicit routes

These always name the resource you want:

  • POST /instances/start
  • POST /instances/{id}/tabs/open
  • GET /tabs/{id}/snapshot
  • POST /tabs/{id}/action

This is the clearest model for multi-instance work.

Shorthand routes

These omit the instance and sometimes the tab:

  • POST /navigate
  • GET /snapshot
  • POST /action
  • GET /text

These route to the "current" or first running instance.

Recommended mental model

For most users, this is the right sequence:

  1. start the server with pinchtab
  2. create a profile if you need persistence
  3. start an instance from that profile
  4. open one or more tabs in that instance
  5. snapshot a tab
  6. act on refs from that snapshot

If you do not need persistence:

  1. start an instance without profileId
  2. use it normally
  3. stop it when done
  4. let PinchTab delete the temporary profile automatically

Example workflows

Workflow 1: persistent logged-in browser

PROFILE_ID=$(curl -s -X POST http://localhost:9867/profiles \
  -H "Content-Type: application/json" \
  -d '{"name":"work"}' | jq -r '.id')

INST=$(curl -s -X POST http://localhost:9867/instances/start \
  -H "Content-Type: application/json" \
  -d "{\"profileId\":\"$PROFILE_ID\",\"mode\":\"headed\"}" | jq -r '.id')

TAB=$(curl -s -X POST http://localhost:9867/instances/$INST/tabs/open \
  -H "Content-Type: application/json" \
  -d '{"url":"https://pinchtab.com/login"}' | jq -r '.tabId')

curl http://localhost:9867/tabs/$TAB/snapshot

Use this when you want cookies and account state to survive instance restarts.

Workflow 2: disposable run

INST=$(curl -s -X POST http://localhost:9867/instances/start \
  -H "Content-Type: application/json" \
  -d '{"mode":"headless"}' | jq -r '.id')

TAB=$(curl -s -X POST http://localhost:9867/instances/$INST/tabs/open \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}' | jq -r '.tabId')

curl http://localhost:9867/tabs/$TAB/text

curl -X POST http://localhost:9867/instances/$INST/stop

Use this when you want a clean, throwaway session.

Summary

The durable object in PinchTab is the profile. The runtime object is the instance. The page object is the tab. The server manages them, and the bridge executes them.