Hire a freelance AI agent to get your work done fast — with open cowork on any Windows PC.
Everything built so far: architecture, auth, per-user VMs, engine selection, pricing tiers, the Run gig chain, phone control, Android profile persistence, channels and the demand pipeline. Updated 2026-08-06.
A customer signs in with Google, gets their own Windows desktop in the browser, describes a gig, and watches an AI agent do the work — taking over or approving at any point. Compute bills by the second; storage persists with the subscription.
The trust layer ("open cowork") is the product's edge: computer-use AI is not 100% reliable yet, so the customer watches it work and the agent pauses before any irreversible action (Submit / Pay / Send). That turns the reliability gap into a feature and lets us ship today.
Transparency you can check — not a staged demo
When we hit a real defect — Claude Desktop’s Google BigQuery connector failing with
redirect_uri_mismatch — we didn’t bury it. We filed a full public bug report to Anthropic with a
reproduction, and published the workaround in the open. That is how we work on your behalf too: problems get
surfaced and documented, not hidden behind a happy-path demo.
Read the upstream issue we filed → anthropics/claude-code #85018
How a request actually reaches your machine. Everything arrives through one tunnel; your files live outside the desktop, so stopping one never loses them.
| Layer | Component | Where |
|---|---|---|
| Edge | Cloudflare tunnel, wildcard *.fastco.work | pve99 |
| Router | Traefik (file provider, per-session routes) | fleet-traefik:8090 |
| Gate | HMAC ForwardAuth (signed session links) | fleet-auth:9000 |
| App | Customer portal + Google sign-in | fleet-portal:9200 |
| Control | Session control plane | fleet-cp:9100 |
| Orchestrator | One-VM-per-user (clone / resume / suspend) | fastco-orch:9500 |
| Agent | Pluggable computer-use engines | fleet-agent:9300 |
| Desktop | dockur Windows + Guacamole per VM | CT130+ |
Why we run Google sign-in directly: Cloudflare Access is seat-metered — free to 50 users, then
$7/user/month, and new users are hard-blocked once seats run out. Fatal for self-serve signup.
So the app runs Google OAuth itself and owns the user table (needed for billing, gig history, and the
user→VM mapping) at ~$0 marginal cost per user. A Supabase broker is still supported as a fallback — the
switch is a single env var (AUTH_MODE) — but production is direct Google, so a customer
goes straight from the app to accounts.google.com and back, with no third party in the login path.
| Surface | Auth | Why |
|---|---|---|
Customers (/app) | Google OAuth, run directly | unbounded, self-serve |
| Admin (control plane, Proxmox) | Cloudflare Access | bounded, free tier |
| Desktop URLs | HMAC signed links | short-lived, no account needed |
The customer never sees a second login. The orchestrator mints an encrypted, signed, 1-hour
assertion via guacamole-auth-json that embeds the RDP connection itself — so there is no
Guacamole account per customer and no shared admin password in the flow.
Every customer gets a dedicated machine — never a shared desktop. Cloud desktops
(the default) are created per user on Google Cloud from a regional golden image; the on-prem
path clones a sealed golden template (CT141, Debian + nesting + /dev/kvm +
dockur Windows + Guacamole) into a per-user LXC. Either way the machine is yours alone and your
storage persists when it stops.
| User | Container | IP | Stack |
|---|---|---|---|
| alice | CT130 cowork-alice | 192.168.1.130 | own Windows + Guacamole |
| bob | CT131 cowork-bob | 192.168.1.131 | own Windows + Guacamole |
| skunpojt | CT132 | 192.168.1.132 | own Windows + Guacamole |
Two gotchas found the hard way. (1) A running container cannot be cloned — the golden
must be stopped and templatized. (2) Clones inherit the template's static IP; the orchestrator now
reassigns 192.168.1.<ctid> after each clone or every user collides on the network.
POST /users/<u>/vm clone-or-resume -> gated URL + SSO deep-link
POST /users/<u>/stop suspend -> billed_seconds, storage persists
DELETE /users/<u>/vm destroy
Compute is metered per running second; suspending stops the meter while the disk stays.
The agent layer is model-swappable — chosen per gig from the launch panel, so hard/high-stakes work routes to a frontier model and cheap bulk work to an open one. All engines share one interface and obey the same two rules: pause before irreversible actions, and yield on human take-over.
| Engine | What it is | Reliability | Cost | Status |
|---|---|---|---|---|
| Claude computer-use | Frontier model driving the screen | highest | ~$2–4 / gig | needs API key |
| Open Cowork | OSS desktop agent, VM sandbox + Skills + MCP | good | self-hosted | needs endpoint |
| OpenCUA | Open-weights CUA (~45% OSWorld) | moderate | GPU only | needs GPU |
| Scripted | Deterministic plan, no model | n/a (demo) | free | working |
Why not Claude Cowork? It is a file/tool agent in Claude Desktop — it does not drive a GUI. fastco.work's wedge is portals with no API, which needs computer-use.
Before pressing Start cowork the customer picks:
| Option | Choices |
|---|---|
| VM size | Small 4 GB/2 vCPU · Medium 8 GB/4 vCPU · Large 16 GB/6 vCPU This sizes the LXC. The Windows guest is given 2 GB less so the Guacamole stack and QEMU overhead fit; on Small that leaves the guest only 2 GB, which is below what Windows 11 needs to be usable. |
| AI engine | Claude · Open Cowork · OpenCUA · Scripted |
| Remote access | optional checkbox — installs RustDesk pointed at our self-hosted relay. (AnyDesk was dropped: its network refused to register these VMs even with full connectivity, because it depends on AnyDesk's own cloud.) |
Deployment is button-triggered, not automatic on login, so nothing is provisioned (or billed) until the customer chooses a configuration.
| Free / trial | Pro | Business | |
|---|---|---|---|
| Desktop | Small (4 GB) | Medium (8 GB) | Large (16 GB) |
| Engine | Scripted / OpenCUA | Claude computer-use | Claude + priority |
| Storage | wiped after session | persists | persists + snapshots |
| Session | time-boxed | suspend/resume | always-on option |
| RustDesk | — | optional | optional |
| Gigs | freight quotes (acquisition) | export docs / CO / Form-D | volume + API |
The strategic point: the freight-quote gig is a loss-leader — at ~฿150–300 it barely clears token cost. Use it to prove "done" cheaply, then upsell document preparation (฿400–1,000 at similar COGS), which is where the margin actually lives.
| Gig | Price | COGS (tokens + infra) | Role |
|---|---|---|---|
| Freight quotes | ฿150–300 | ~$2.5–4.5 | acquisition, thin |
| Export docs / CO / Form-D | ฿400–1,000 | ~$2–5 | profit driver |
Self-hosted desktops cost ~$5–8 per instance/month versus $70–150 for a comparable cloud Windows VM — a 10–20× spread that is the margin. The real constraint is gig throughput and reliability, not node cost.
| Item | Status |
|---|---|
| Landing, business plan (TH/EN), brief | live |
| Cloudflare wildcard + Traefik fleet edge | live |
| HMAC session gating | verified 403/200 |
| Control plane + per-second billing | verified |
| Google sign-in (published) | verified end-to-end |
| One VM per user + isolation | verified |
| SSO into desktop (no 2nd login) | verified — guac JSON auth, per-VM key |
| Launch config (size / engine) | verified on a clone |
| Per-VM remote-access ID (RustDesk, self-hosted) | verified — ID minted |
| Daily fleet software refresh | timer active |
GCP backend (?backend=gce, nested-virt GCE VM) | verified — ~2.5min to a gated desktop |
| GCP golden = imported pve99 image (PD snapshot) | verified — same bake, both clouds |
| Daily GCP golden refresh | runs unconditionally, verified |
Persistent D: / burner (?storage=) on GCP | pct only — not yet on GCE |
Storage/compute split (?storage=persist|burner) | verified — data survives VM destroy |
| In-guest reboot / Hyper-V watchdog recovery | verified — hang detected + auto-recovered |
| Agent loop against a live model | needs API key |
| Per-VM secrets (SSO key, Windows password) | placeholders |
| Phone control — Android via chat (Models A & B) | verified — both drive a real phone |
| Per-user storage isolation (server-scoped SMB) | verified — cross-tenant read denied |
| Agent activity panel (live actions + screenshot) | live |
| Human-in-the-loop approval gate (pause before Submit/Pay/Send) | planned — not built (see note) |
| Move off residential ISP | before launch |
# provision / suspend / destroy a user's VM (on pve99)
K=$(grep CP_API_KEY /opt/fleet/.secret.env | cut -d= -f2)
curl -X POST -H "X-API-Key: $K" localhost:9500/users/<user>/vm?size=medium
curl -X POST -H "X-API-Key: $K" localhost:9500/users/<user>/stop
curl -H "X-API-Key: $K" localhost:9500/users
# fleet services
docker ps --filter name=fleet
systemctl status fastco-orch
# verify the live login path really points at Google (not a broker)
curl -s -o /dev/null -w "%{redirect_url}" https://fastco.work/app/api/auth/start
Known gotcha: Google returns the real OAuth error in the response body, not the HTTP status — a misconfigured client id/redirect still comes back 200. Verify functionally with the redirect check above, and read the body on failure, rather than trusting the status code.
The chat panel on /app is one textarea, one Send button, and a
log — no instructions printed inline. A single input drives everything: it
dispatches a gig to a fastco desktop, or runs a command on a selected BYO machine,
depending on which target is picked (there is no separate command field). What it does:
start the cowork desktop first, then Run gig hands whatever is in the textarea to
the AI running on it as its next instruction. With 2+ desktops open at once, a "Send to"
picker appears above the textarea so a gig can be aimed at a specific one instead of
guessing from whichever happens to be on screen — hidden entirely when there is only one
desktop, since there is nothing to choose between.
This is the chain that turns that textarea into work happening on a desktop:
[browser] Run gig
-> POST /app/api/gig (portal, cookie-authenticated)
-> POST /users/<slug>/gig (orchestrator, on the Proxmox host)
-> writes provision.cmd into that user's /opt/cowork/shared
-> \\host.lan\Data\provision.cmd (same folder, seen from inside Windows)
-> fastco-provision scheduled task (SYSTEM, every minute) runs it
-> activate.cmd starts the chosen engine with the goal
Why the orchestrator and not the agent service. The agent runs in a container
with no pct, so it cannot reach a VM. Giving that container host privileges
purely to dispatch a string would have widened the blast radius for no benefit, so
dispatch lives in the orchestrator, which already runs on the host.
Why a file drop and not an API inside Windows. The guest exposes no inbound service by design — the only ports reachable are RDP (via Guacamole) and the relay. A file on the shared folder needs no listener, survives guest reboots, and the agent's run-once marker is the file's MD5, so re-issuing a different gig re-runs automatically while a retry of the same one does not.
Known limitation: provision.cmd is a single slot —
last write wins. Two gigs issued inside the same ~60 s poll window mean only the second
one runs. A per-gig queue directory is the fix; it is not built yet.
Not Windows-only any more. The same Send routes to whichever backend the target runs on: a Windows guest picks the gig up via its shared-folder file drop (above), a Linux burner and the ARM Play Store desktop get a terminal opened on them running the agent directly, and a phone-equipped desktop drives the Android phone (next section).
A desktop can carry an Android 16 phone with Google Play — either the cloud ARM "Android 16 + Google Play" machine, or a Linux burner with the Android add-on. You control that phone by chatting, and you choose how on the setup panel under "Phone control":
How Model A connects. The bridge APK ships pre-installed in the image; connecting
is a one-time headless pairing done for you at dispatch: enable its accessibility service,
launch it once to mint a pairing code, read that code from the app's own storage, forward
its port, and thereafter talk to it over token-authenticated HTTP on the desktop's loopback.
Endpoints: /screenshot, /tap, /tap_text,
/type, /swipe, /press_key, /open_app,
/find_nodes. Both models work on x86 burners and the ARM Play Store desktop.
Cost. Phone control is free-first: it tries a chain of free vision models (different providers) and only falls back to a cheap paid model if the whole free chain is momentarily congested — so it stays reliable at essentially no cost.
Planned (not yet built): a real approval gate. A human-in-the-loop control where the agent genuinely PAUSES before an irreversible action (Submit / Pay / Send) and waits for your explicit approval. An earlier UI implied this but did not enforce it, so that control was removed rather than ship a safety promise the system did not keep. Building it for real needs an approval hook the in-desktop agents check before acting; it is on the roadmap, not live.
Watch it work. While a phone gig runs, an Agent activity panel under the chat log shows each action in plain words ("open app Settings", "tap 'Network'", "done") alongside a live screenshot of the phone, refreshing every couple of seconds — so you can confirm the chat is actually driving the phone.
Four things decide whether a chat instruction actually moves the phone. Each was found the hard way on 2026-08-06.
input tap, keyevent and text all report success and do nothing, and the accessibility tree is restricted too. Every desktop now runs phonectl ready at gig start, which marks the device provisioned and disables the wizard./opt/fastco/PLAYBOOK.md, plus your own private notes at ~/persist/.fastco/NOTES.md, which are redacted before they reach any model provider.A Google sign-in used to die with the desktop, so every session began signed out. The phone’s /data now lives on a local volume and is archived to your own storage when the desktop stops, then restored when it starts again. Signing in once is enough, and apps you install from Play are still there next time.
Verified end to end: write a marker, destroy the desktop, provision a new one, read the marker back intact. The archive is written on a clean stop — a desktop that is force-killed may lose changes made since its last snapshot.
When you send an instruction to an Android desktop, the agent has three ways to find the controls on screen. It tries them in order, and only the last one involves any guessing.
Why this matters to you. Some apps deliberately expose nothing to the accessibility layer. A shopping app may show a fully rendered login form while reporting no controls at all. In that case the agent falls back to reading the screen visually, which works but is slower and less precise than tapping a named button.
A web page inside an app is a special case. To Android, the entire page is a single rectangle — every field and button inside it is invisible to the accessibility layer. That is Android's design, not a limitation of your desktop. The agent works around it by asking the page directly, which restores exact control positions.
Practical guidance. If a task on one app is noticeably slower or less reliable than the same task elsewhere, this ordering is usually why. Naming the control you want (“tap Log In”) helps in every case, because an exact label can be matched against sources 1 and 2 rather than estimated from pixels.
Two states look like failure but are not. Knowing which is which saves you starting over.
Building a machine takes around a minute and a half. Our edge gives up waiting at about a hundred seconds, so on a slow build you can see a timeout while the machine is being created normally. Do not press start again — that builds a second machine you will be charged for. Wait a few seconds and reload the page; your desktop will be listed and running.
When a task reaches something only you can do — approving a sign-in on your phone, a verification link sent by SMS, a one-time code by email — the agent stops and waits. That is deliberate: it will not guess at a security step on your behalf.
While it waits, your desktop is held open and will not be shut down for inactivity, so you can take your time. Respond on your phone, then tell the agent to carry on. Earlier versions treated a waiting desktop as an abandoned one; they no longer do.
Reload fastco.work/app first — the desktop keeps running
independently of the page showing it, so a reload fixes most display problems
without losing any work. If the panel stays blank after a reload, stop the
desktop and start it again; anything on your persistent drive is unaffected.
Live as of 2026-08-12. A WhatsApp chat can start, stop and check a cowork.
Commands: start [small|medium|large] · stop · status ·
gigs · help · ping. Every reply is a quoted reply with a
🤖 header naming the command it answers. Verified the day it shipped: a real
start small sent from a phone provisioned a cloud Windows desktop in ~3.5 minutes,
returned the gated session link into the same chat, and stop suspended it with
per-second billing (billed 183s) — the full loop, no staged steps.
How linking works (the flow we actually shipped): Honest status: today this runs for the first linked account; self-service pairing for every customer is the next build — the flow below is exactly what it will use.
One person = one WhatsApp = one desktop. Each customer links their own WhatsApp to their own machine — the same flow above, per person. There is no shared business number in this path, which is also why volume per number stays personal-scale. Only the linked owner's number (and its WhatsApp LID alias) is allowed to command; anyone else who messages that number is silently ignored. The send/session API is gated by its own key, separate from any site password.
Two transports, one command core. Today's channel runs on WhatsApp Web
device-linking (WAHA). The same command module also speaks the official WhatsApp
Business Cloud API — that path activates the day Meta business verification and a
production number exist, with no code changes. The trade-offs, honestly: device-linking
needs no Meta approval and costs nothing, but is unofficial — fine at personal volume,
not for bulk messaging. The Cloud API is ToS-clean and made for scale, but is still
blocked on Meta's review (WABA: REJECTED, sandbox number banned).
Implementation notes for whoever builds on it: the sender arrives as a WhatsApp
LID (2474…@lid), not the phone number — allowlists must carry both.
Session auth lives in the gateway container and is backed up host-side; recreating the
container without restoring it forces a re-pair. A successful send on the Cloud API
returns a wamid meaning accepted, not delivered — delivery is a
separate status webhook.
The market data behind the landing page and /api/gigs.json is collected by
CT122 across 12 demand platforms — Fastwork Jobboard (Thailand), Freelancer.com and Guru (a client posts a project
for a freelancer), plus Jobicy, Arbeitnow, RemoteOK, Remotive, WeWorkRemotely and
WorkingNomads (companies hiring remote staff).
Direction matters more than volume. Every row must be a listing where a client is seeking a freelancer. Two sources were the wrong way round and have been corrected:
| Source | What it actually returns | Action |
|---|---|---|
| Fastwork.co (Thailand) | Its search.productSearch API returns freelancers selling services —
base_price, rating, purchase_count,
/user/-/<slug> seller URLs, and Thai titles beginning
รับ… ("I accept this work"). That is supply, the opposite side of
the market. |
Reclassified as supply. Kept as Thai pricing evidence in its own
supply_benchmark section (292 listings, median ฿900), never in gigs. |
| Toptal | The scraper was collecting marketing pages — "Resume Builder", "Cover Letter Builder", "ATS Resume Checker". Not job listings at all. Its real board is JS-rendered with nothing in the server response. | Removed (15 rows deleted). |
Correction. An earlier version of this page claimed Fastwork had no
public demand-side board. That was wrong, and the reasoning is worth recording: every probe
was made against paths under fastwork.co, they all 404'd, and absence of evidence
was taken as evidence of absence. The job board is a separate subdomain, so it could
never have appeared in those probes.
| Fastwork surface | Host | Side of the market |
|---|---|---|
Marketplace (search.productSearch) |
api.fastwork.co |
Supply — freelancers selling services |
Job board (GET /api/jobs) |
jobboard-api.fastwork.co |
Demand — clients posting jobs |
Found by reading the job board's own frontend bundles for absolute URLs rather than guessing
endpoint names. The board paginates the full set:
GET https://jobboard-api.fastwork.co/api/jobs?page=N&page_size=50 →
meta.total_count 3,421 open posts across 69 pages, no auth.
Each record is unmistakably demand: status:"open", a THB budget,
freelance_offers_count (freelancers bid on it), and titles of the form
ฉันหา… ("I'm looking for…"). The type field splits it —
about 70% are freelance, the rest part-time, contract or full-time — so each
row sets its own listing_type instead of inheriting one from the platform.
Every row carries a listing_type:
demand_freelance (6,344) · demand_employment (1,332) ·
supply (293). Published totals count demand only: 7,676. An unknown new
source defaults to unclassified, not demand, so it can never silently inflate
the figure.
Ten more platforms were swept for demand surfaces on subdomains rather than paths.
Most yielded nothing: the extra hosts were CDNs (cdn.useme.com,
assets.arc.dev) or support sites, and generic API probes returned 403/404.
Two real wins:
| Change | Effect |
|---|---|
Freelancer.com paginated. It was taking limit=50 with no offset
while total_count for a single query is ~1,200 — about a 4% capture
rate. Now walks offset until a short page. |
530 → 1,970 |
JobsDB Thailand added — SEEK's public search API, no auth:
th.jobsdb.com/api/jobsearch/v5/search?sitekey=TH-Main&sourcesystem=houston.
Mostly permanent roles, so rows classify per listing off workTypes. |
460 new (25 gig, 435 permanent) |
Deliberately excluded: Upwork and Fiverr both carry real demand (Fiverr's Buyer Requests especially), and Contra/Arc.dev return 403 on their APIs. All of them actively block automated collection, and getting past that means building bot-detection evasion — out of scope by choice, not capability.
Subdomain sniffing only sees hosts named in JS bundles and misses POST endpoints entirely (Fastwork's marketplace API is a POST). The general method is to load each site in a real browser and record every JSON response — no stealth, no anti-detection; if a plain browser is blocked, that is the platform's answer and it stays out.
Across ten more platforms this caught mostly taxonomy calls, because their job lists
are server-rendered so there is no XHR to intercept. That was still enough:
POST api.truelancer.com/api/v1/projects/links returned only skill links, but it
revealed the API host — and the job endpoint sits beside it:
GET https://api.truelancer.com/api/v1/projects?page=N # no auth, 15/page
Truelancer: 375 client-posted projects with budget, currency and skills.
CrowdWorks JP exposes only /api/v3/public/skill/all — its jobs path 404s with the
correct Accept, so it is server-rendered only and stays out. Useme,
PeoplePerHour, Guru, Wishket, Projects.co.id and OnlineJobs.ph made no job XHR at all;
Lancers returned 405.
The remaining platforms make no job XHR because the listings arrive in the initial
document. The usual structured hiding places were all checked —
ld+json (schema.org JobPosting), __NEXT_DATA__,
window.__NUXT__, __INITIAL_STATE__. None of them carried job
data on any of the nine sites. They are plain server-side templates, so the only route
is HTML parsing, with all the fragility that implies.
Checking the markup before writing scrapers was worth it — most would not have been worth building:
| Platform | Markup check | Outcome |
|---|---|---|
| Useme (PL/EU) | 20 detail links + 20 matching anchor titles per page, stable
/en/jobs/<slug>,<id> pattern, clean ?page=N |
Built — 500 jobs |
| PeoplePerHour | 31 links, but the titles are category nav ("AI Services", "Writing & Translation") | Not job rows |
| OnlineJobs.ph | 30 links, every title is "See More" | Nav noise |
| Wishket (KR) | 10 project links, no anchor titles | Deferred |
| Projects.co.id · CrowdWorks · Workana | 0 detail links in HTML | JS-rendered |
| Lancers (JP) | HTTP 405 | Blocked |
Useme keys on its anchor pattern rather than CSS classes, which is the part of a page that churns least — Guru's scraper already broke once when its URL and selector changed. Its budget is stored NULL on purpose: the list-page prices cannot be reliably attributed to a row, and a wrong number is worse than no number for pricing evidence.
True gig demand now: Fastwork Jobboard 3,163 · Freelancer.com 1,970 · Useme 500 · Truelancer 375 · Guru 311 · JobsDB Thailand 25. Thailand alone contributes 3,188 freelance posts — the largest share of the pipeline, and the beachhead market.
Adding the job board took Thai freelance demand from 0 to 3,163 posts — it is now the single largest source in the pipeline, and it is the beachhead market. Fastwork's marketplace data stays classified as supply and remains useful as the ฿900-median pricing benchmark: the platform simply has both sides, on different hosts.
API first, scrape only as fallback, per platform rather than globally: an early version treated one platform's success as proof the whole run had worked, so nine collectors were skipped silently.
Upwork and Fiverr are deliberately excluded. Both actively block automated collection, and working around that would mean building bot-detection evasion — out of scope by choice, not by capability.
The repo previously shipped synthetic sample data (0% real URLs, ~80% templated strings). It was removed once verified against live responses; everything served now comes from a real fetch.
A customer can attach a machine they already own — a laptop, a workstation, a server — instead of renting a desktop from us. The agent runs as their own user account, not as root, and can do nothing that account could not already do. Access is revoked from the dashboard at any time.
Install — one command, on the machine being attached. The token comes from the dashboard and is single-use:
curl -fsSL "https://byo.fastco.work/install.sh?t=<token>" | sh -s <token>
The installer refuses to run as root, pins the agent by sha256 and
verifies it before making it executable, then registers a per-user systemd service —
enabling linger so the agent survives logout, and verifying the process
is actually running rather than trusting the exit code. If systemd is unavailable
or the user cannot enable lingering, it falls back to a background process and says so.
Both the installer and the agent are refused without a valid enrolment token or device
credential, so neither is publicly downloadable.
Attach your fastco drive — optional, and the reason most people do this:
agent.py storage-attach
This generates a WireGuard keypair (kept on the machine — only the public key is
ever sent), brings up a tunnel to the storage server, and mounts your own private SMB
share. AllowedIPs is scoped to the storage host only: your ordinary
internet traffic is not routed through us, which is deliberate — you asked for a network
drive, not a VPN. The share is per-tenant: your credential reaches your directory and
nothing else, enforced both by Samba (valid users) and by per-user groups
with 0700 directories on disk.
Windows — prerequisite, please read before trying: the agent supports
Windows (it mounts the drive with net use and drives the WireGuard
service), but there is no install.exe yet, and the agent needs
Python 3.7 or newer already installed. Get it from
python.org
and tick “Add python.exe to PATH” during setup. Then place
agent.py and run python agent.py enroll <token>.
The agent uses only the Python standard library — no pip, no packages to
install, nothing to resolve — so a single bundled .exe is
straightforward and is the planned fix. Until it ships, the Python install above
is the one manual step.
Questions the FAQ didn't cover, or interested in the customizable Max plan?
Message us on WhatsApp or call us at +66 86 954 1666
Windows licensing. fastco.work provides the VM infrastructure a Windows desktop runs on — we do not sell, distribute, or license Windows itself. Windows installs from Microsoft’s own servers via the open-source dockur project our desktops are built on, and you are responsible for holding a valid Windows license and complying with Microsoft’s licensing terms. Windows and related names are trademarks of Microsoft Corporation; fastco.work is not affiliated with, sponsored by, or endorsed by Microsoft. Bring your own machine (BYO-VM) to run any OS under its own license.
© fastco.work