> For the complete documentation index, see [llms.txt](https://fleet.hackmap.win/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://fleet.hackmap.win/getting-started/quickstart.md).

# Quickstart

Five terminals, ten minutes. By the end you'll have a master, two workers, and a running automation producing data into a queue you can pop from.

## 1. Install

```bash
git clone https://github.com/sarperavci/fleet && cd fleet
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
pip install -e examples/hello-world
```

## 2. Run Redis

```bash
docker run -d --name fleet-redis -p 6379:6379 redis:7-alpine \
  redis-server --appendonly yes --save 60 1
```

## 3. Start the master

In terminal 1:

```bash
export FLEET_ADMIN_TOKEN=admin-secret-please-rotate
export FLEET_WORKER_TOKEN=worker-secret-please-rotate
fleet master
```

The master prints "Uvicorn running on <http://0.0.0.0:8000>". Open that URL in a browser. You'll see one tab labelled `hello-world` with an empty worker table.

## 4. Start a worker

In terminal 2:

```bash
export MASTER_URL=http://localhost:8000
export FLEET_WORKER_TOKEN=worker-secret-please-rotate
fleet worker --type hello-world --id w1
```

The worker logs "ws connected" and then polls for config. The dashboard shows `w1` with state `IDLE` and zero slots. That's expected: there's no config yet.

## 5. Push a config

In terminal 3:

```bash
curl -X PATCH http://localhost:8000/api/v1/automations/hello-world/workers/w1/config \
  -H "Authorization: Bearer admin-secret-please-rotate" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "slots": 2, "greeting": "hello", "interval_seconds": 1}'
```

Response:

```json
{"config": {"enabled": true, "slots": 2, "greeting": "hello", "interval_seconds": 1}, "config_gen": 1}
```

The master pushes a `config_changed` frame to the worker over WebSocket. The worker re-polls, validates the config against `HelloConfig`, sees `enabled=true, slots=2`, and runs `cold_start`. Two slot tasks begin emitting greetings every second.

Watch the worker log: you'll see `slot 0 up`, `slot 1 up`, and then quiet steady running.

## 6. Pop outputs

In terminal 4:

```bash
curl -X POST http://localhost:8000/api/v1/automations/hello-world/output/pop \
  -H "Authorization: Bearer admin-secret-please-rotate" \
  -H "Content-Type: application/json" \
  -d '{"n": 5}'
```

You get back five oldest entries:

```json
[
  {"id": "...", "slot_id": 0, "ts": 1778...,  "payload": {"greeting": "hello", "from_slot": 0, "nonce": "abcxyz"}},
  ...
]
```

Each `pop` removes them from the stream. If no consumer pops, the stream caps at 50k entries (configurable) and the oldest get evicted.

## 7. Scale, change, stop

Change `slots` and watch the worker scale without restarting:

```bash
# scale up
curl -X PATCH .../w1/config -d '{"slots": 4}' ...
# stop everything
curl -X PATCH .../w1/config -d '{"enabled": false}' ...
```

Each PATCH bumps `config_gen` and the worker reconciles to the new desired state.

## 8. Try the CF harvester

The `hello-world` automation is just a "two slots, emit dummy payloads" demo. The headline use case is harvesting Cloudflare clearance cookies into a shared pool so a fleet of scrapers can use them. That's all in `examples/cf-cookie-harvester` — a real browser, a real CF challenge.

Stop the hello-world worker (`Ctrl-C` in terminal 2), then install the harvester triplet:

```bash
pip install -e .
pip install -e examples/cf-contracts
pip install -e examples/cf-cookie-harvester
```

The base install already includes `cloakbrowser` (Playwright + a patched Chromium 146, \~440 MB downloaded on first launch), `mitmproxy`, `curl_cffi`, and `pyvirtualdisplay`. Auto-managed Xvfb means you don't need to start one yourself; see [Browser-based automations](/guides/browser-automations.md).

Start a worker for the new automation:

```bash
fleet worker --type cf-cookie-harvester --id harvest-1
```

Enable it (single slot, headed browser):

```bash
curl -X PATCH http://localhost:8000/api/v1/automations/cf-cookie-harvester/workers/harvest-1/config \
  -H "Authorization: Bearer admin-secret-please-rotate" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "slots": 1, "headless": false}'
```

Submit a harvest task:

```bash
curl -X POST http://localhost:8000/api/v1/automations/cf-cookie-harvester/tasks \
  -H "Authorization: Bearer admin-secret-please-rotate" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"site": "nopecha.com", "url": "https://nopecha.com/demo/cloudflare", "timeout": 60}}'
```

The harvester opens Brave, navigates to the demo, clears the Cloudflare challenge, and deposits a `CfCookie` into the `cf-cookies` pool. Watch the worker log — you'll see something like `harvested nopecha.com in 5.0s (cookies=['cf_clearance'])`.

Verify it landed in Redis:

```bash
redis-cli HGETALL fleet:pool:cf-cookies
```

You'll see one hash field with the base64-encoded JSON payload and tags `{"site": "nopecha.com", "fresh": true}`. The `fleet_pool_size{pool="cf-cookies"}` line in `/metrics` will now read `1`.

Any other automation that imports `COOKIE_POOL` from `cf-contracts` can claim that cookie via `ctx.pool(COOKIE_POOL).where(site="nopecha.com").claim(hold_seconds=20)`. That's the producer/consumer pattern the framework was built around.

## Next

* [Your first automation](/getting-started/first-automation.md) — write your own instead of using the example.
* [Architecture](/concepts/architecture.md) — what's actually happening end-to-end.
* [CF cookies pool walkthrough](/guides/cf-cookies-pool.md) — the producer/consumer pattern in detail.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://fleet.hackmap.win/getting-started/quickstart.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
