fastco.work Pricing FAQ Live demand VM pricing Business plan Docs Setup guide Open app

fastco.work — build documentation

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.

Edge Cloudflare tunnel + Traefik router + the auth gate — one way in Services portal · auth · control plane · orchestrator · billing · channels Windows Linux burner Android 16
The whole platform in three layers — the detailed wiring is in section 2.
Contents
  1. What it is
  2. Architecture
  3. Auth & sign-in flow
  4. One VM per user
  5. Cowork engine selection
  6. Free vs paid tiers
  7. Unit economics
  8. Build status
  9. Operations runbook
  10. Run gig — how an instruction reaches a desktop
  11. Phone control — Android via chat (Models A & B)
  12. Channels — WhatsApp
  13. Demand pipeline

1 · What it is

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

fastco.work console
The cowork console — gig brief, configuration, session panel, cowork controls, embedded desktop and agent log.

2 · Architecture

How a request actually reaches your machine. Everything arrives through one tunnel; your files live outside the desktop, so stopping one never loses them.

YouPhone or laptopchat, or the consoleThe edgeCloudflareTLS + tunnelTraefikrouter + auth gatefastcoConsole /appsign-in, launch, chatYour machine — pick oneWindowsfull desktopLinux burnerfree, RAM onlyAndroid 16real Play StoreYour storagepersists between sessionsYour own machineBYO — optionalevery requestsigned inyou press StartD: driveon hardware you ownA green dot marks an authentication gate. Dashed lines are optional paths.Your files live outside the machine, so stopping a desktop never loses them.
Sign-in is Google directly. Pick Windows, a free Linux burner, or Android 16 with the real Play Store — your storage is the same either way.
LayerComponentWhere
EdgeCloudflare tunnel, wildcard *.fastco.workpve99
RouterTraefik (file provider, per-session routes)fleet-traefik:8090
GateHMAC ForwardAuth (signed session links)fleet-auth:9000
AppCustomer portal + Google sign-infleet-portal:9200
ControlSession control planefleet-cp:9100
OrchestratorOne-VM-per-user (clone / resume / suspend)fastco-orch:9500
AgentPluggable computer-use enginesfleet-agent:9300
Desktopdockur Windows + Guacamole per VMCT130+

3 · Auth & sign-in flow

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.

SurfaceAuthWhy
Customers (/app)Google OAuth, run directlyunbounded, self-serve
Admin (control plane, Proxmox)Cloudflare Accessbounded, free tier
Desktop URLsHMAC signed linksshort-lived, no account needed
Google account chooser
Google sign-in — the consent screen is published to production, so any Google account works.
OAuth published
OAuth consent screen moved from Testing to In production (was limited to 3 test users).

Single sign-on into the desktop

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.

Windows desktop embedded
The live Windows 11 desktop rendering inside fastco.work/app — one login, no Guacamole prompt. (Session token redacted.)

4 · One VM per user

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.

UserContainerIPStack
aliceCT130 cowork-alice192.168.1.130own Windows + Guacamole
bobCT131 cowork-bob192.168.1.131own Windows + Guacamole
skunpojtCT132192.168.1.132own 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.

Lifecycle & billing

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.

5 · Cowork engine selection

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.

EngineWhat it isReliabilityCostStatus
Claude computer-useFrontier model driving the screenhighest~$2–4 / gigneeds API key
Open CoworkOSS desktop agent, VM sandbox + Skills + MCPgoodself-hostedneeds endpoint
OpenCUAOpen-weights CUA (~45% OSWorld)moderateGPU onlyneeds GPU
ScriptedDeterministic plan, no modeln/a (demo)freeworking

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.

Launch configuration

Before pressing Start cowork the customer picks:

OptionChoices
VM sizeSmall 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 engineClaude · Open Cowork · OpenCUA · Scripted
Remote accessoptional 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.

6 · Free vs paid tiers

Free / trialProBusiness
DesktopSmall (4 GB)Medium (8 GB)Large (16 GB)
EngineScripted / OpenCUAClaude computer-useClaude + priority
Storagewiped after sessionpersistspersists + snapshots
Sessiontime-boxedsuspend/resumealways-on option
RustDeskoptionaloptional
Gigsfreight quotes (acquisition)export docs / CO / Form-Dvolume + 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.

7 · Unit economics

GigPriceCOGS (tokens + infra)Role
Freight quotes฿150–300~$2.5–4.5acquisition, thin
Export docs / CO / Form-D฿400–1,000~$2–5profit 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.

8 · Build status

ItemStatus
Landing, business plan (TH/EN), brieflive
Cloudflare wildcard + Traefik fleet edgelive
HMAC session gatingverified 403/200
Control plane + per-second billingverified
Google sign-in (published)verified end-to-end
One VM per user + isolationverified
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 refreshtimer 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 refreshruns unconditionally, verified
Persistent D: / burner (?storage=) on GCPpct only — not yet on GCE
Storage/compute split (?storage=persist|burner)verified — data survives VM destroy
In-guest reboot / Hyper-V watchdog recoveryverified — hang detected + auto-recovered
Agent loop against a live modelneeds 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 ISPbefore launch

9 · Operations runbook

# 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.

10 · Run gig — how an instruction reaches a desktop

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).

10a · Phone control — Android via chat (Models A & B)

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.

10b · Phone control — what makes it reliable

Four things decide whether a chat instruction actually moves the phone. Each was found the hard way on 2026-08-06.

10c · Android profile persistence

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.

10d · How the phone agent reads the screen

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.

  1. Accessibility tree — the app publishes its own controls, with exact labels and positions. This is what most native Android apps do, and it is by far the most reliable: the agent taps a real button by name.
  2. Web page contents (DevTools) — if the screen is a web page inside a browser or WebView, the page itself is asked for its controls. This also gives exact positions.
  3. Looking at the screen — a screenshot, read by a vision model. Used only when neither source above can see anything.

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.

10e · If your desktop seems stuck

Two states look like failure but are not. Knowing which is which saves you starting over.

“Gateway time-out” while starting a desktop

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.

Your desktop is waiting for you, not stuck

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.

Still not right?

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.

11 · Channels — WhatsApp

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.

  1. The system generates an 8-character pairing code for your number (we default to the code, not the QR — on a phone with flaky data the QR expires mid-scan; the code worked first try where the QR failed twice).
  2. On your phone: WhatsApp → Settings → Linked devices → Link a device → Link with phone number instead → type the code.
  3. Your account shows a new linked device; from then on, messages you type in your own chat (or that the system sends there) drive your desktop.

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.

12 · Demand pipeline

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:

SourceWhat it actually returnsAction
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 surfaceHostSide 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=50meta.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.

Expansion — applying the same method to other platforms

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:

ChangeEffect
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.

Runtime API tracing — the general form of the trick

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.

Server-rendered sites — what the third technique did and didn't yield

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:

PlatformMarkup checkOutcome
Useme (PL/EU) 20 detail links + 20 matching anchor titles per page, stable /en/jobs/<slug>,<id> pattern, clean ?page=N Built — 500 jobs
PeoplePerHour31 links, but the titles are category nav ("AI Services", "Writing & Translation")Not job rows
OnlineJobs.ph30 links, every title is "See More"Nav noise
Wishket (KR)10 project links, no anchor titlesDeferred
Projects.co.id · CrowdWorks · Workana0 detail links in HTML JS-rendered
Lancers (JP)HTTP 405Blocked

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.

13 · Bring your own machine (BYO)

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.

Get in touch

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