Changelog
What changed in the forculus CLI and in each SDK, from one release to the next, newest first. The platform itself changes without releases, and the docs say what it does today.
The forculus CLI
Section titled “The forculus CLI”forculus, from npm, Homebrew or the install script. Its docs.
0.1.14, 19 October 2026
Section titled “0.1.14, 19 October 2026”Changed:
forculus schedule offsays when it cancelled a run that was waiting to go, fromforculus schedule runor because it was due. That run never happened, and nothing said so.forculus auditsays it too.
Fixed:
forculus webhook deliveriestimes each delivery by when its event came, the order it lists them in. It gave the last try’s time, so an older event tried again read as newer than the ones above it. One that took more than one try says how many.forculus webhook deliverieslists events in the order they happened, newest first, each with the time it happened. Events we picked up in the same minute came in any order, all with the time we picked them up, up to a minute late.--jsonhas each one’sevent_at, and the dashboard’s list uses the same time.forculus webhook list,webhook addandwebhook editsay “all events” for a webhook that gets every event, and how many for one that gets more than three. They printed every name on one line, all 158 for a webhook added without--event.--jsonstill has every name.webhook editsays only what it changed, not the whole row after it.- Signed in to staging, the CLI’s links and examples are staging’s.
webhook add,audit-stream add,log-drain add, a redirectforculus curlexplains,forculus uninstallandforculus mcp installpointed at production’s docs, and the preview a viewer is asked for was on production’s apps domain. The docs are on the site of the API in use, and the preview’s example on the apps domain that API names. forculus curl --helpand the docs say it exits 0 whatever the app answers, a 404 or a 502 included, as curl does, and that--failexits 22 on a 4xx or 5xx.forculus auditandforculus watchname a preview reached through the apps domain, asforculus curl --previewreaches one, as they name one opened at its own address:on a preview of hello (p-0a1b2c3d), with the path the preview saw. It read as a request to the live app, with the apps domain’s path.
0.1.13, 19 October 2026
Section titled “0.1.13, 19 October 2026”Added:
forculus curl <workspace>/<app> --preview <version>sends the request to one of the app’s previews, as you, through the apps domain. It also takes the preview’s address, for someone the app is shared with who can’t list its versions.forculus open --preview <version>prints the preview’s address. The MCP server’scall_apptakespreview, as a number or a string. The agent plugin’s preview skill uses it, so update the CLI before the plugin.
Changed:
- A container deploy and
forculus dev --dockergive the image they build a name of their own, and remove it by that name once they’re done. They removed it by its id, which deletes the image whatever single name it has by then: two deploys of the same code at once could delete the image the other was about to save, or one you had named since the build began. Docker now keeps an image for as long as anything else names it. forculus deployrefuses--port,--instance-typeand--instancesfor a Worker or a static site, before anything is built or uploaded. It deployed without them and said nothing.--dry-runrefuses them too, and so does the MCP server’sdeploy_app, by its own names for them.forculus deployrefuses--mainand--compatibility-datefor a container or a static site the same way. It dropped them without a word too.- When
--kindwas given, a flag for another kind says to change--kind, not to add it. Adding a second one failed. forculus detect,--dry-runand the start of a deploy printsize:andcontainers:afterport:when forculus.json setscontainer.instance_typeorcontainer.instances.--dry-runand the start of a deploy give the port, size and count that--port,--instance-typeand--instancesset over forculus.json’s. They gave the file’s, which the deploy didn’t use.- The MCP server’s
deploy_appschema givesinstancesas 1 to 20, the numbers it takes. It said 0 to 65535. forculus scalesays so on a line of its own when the platform rolled the live version out again because the last deploy didn’t finish. Otherwise a scale rolls nothing out, with any CLI: the containers running keep running, even after an earlier push failed.forculus scale --helpsays what a scale changes: nothing rolls out unless the last deploy didn’t finish, and containers past the new number stop within a few minutes.--helpsplits the sentences it joined with a semicolon.- What
forculus mcp’s tools change is in the audit log asvia: mcp, notcli. Aforculuscommand an agent runs in a shell is stillcli. The server says so in its User-Agent,forculus/<version> mcp, which marks the entry and proves nothing.forculus watchshows it as(mcp). forculus deploy --dry-runshows the entry and the compatibility date that--mainand--compatibility-dateset.forculus detectand the dry run printmain:andcompatibility date:lines for a Worker when something sets them.- A kind from forculus.json reads
because forculus.json says it's a container, where it saidyou said. With a kind given, the first line still names the framework detection found. forculus import --env-filereads a quoted value over more than one line whole, and leaves it out as Not set. It stopped at the second line, or read it as a variable of its own.forculus importsaysThe directory <dir> already has files in itrather than starting with the directory’s name, which was given a capital. A dry run says why it stops there too.forculus kv putandforculus kv rmsay a change can take a minute or so to show everywhere.kv rmsaid the key was gone whilekv getstill read it.forculus mcp install cursornames a Homebrew install by itsoptpath, which lasts throughbrew upgrade, not the version’s own Cellar path.forculus dev --as webhookgives a delivery id and a signed time only for Stripe, Slack and Standard Webhooks, as the platform does. GitHub, Shopify and HMAC-SHA256 deliveries had both.- The MCP server:
list_cli_sessionsgives times as RFC 3339,workspace_insightstakeshour,day,weekandmonthfor its window,query_databasenamesrun_sqlfor a write,delete_kv_keyandset_kv_valuesay a change takes a minute or so to show, a missing key nameslist_kv_keys, and a directory outside the session’s says what to do for an upload, an import or a clone. A folder the client names as the plugin’s is never deployed from, whichever root reaches it. forculus env setandenv removeon a container app, and the MCP server’sset_env, say the container takes about five minutes to restart with the change. They said a minute or two, and it took five and a half on production every time.
Fixed:
forculus curlprinted200 OKfor a stream cut off because your access to the app ended. It now says the answer was cut off and exits with code 1. Only a request sent as you is checked this way: one sent with a bypass token, and any other HTTP client, can still see a stream the platform cut as one that finished.forculus curltold someone refused with a404to runforculus app liston the app’s workspace, whether or not they were in it. Someone outside it is pointed atforculus app list --sharedinstead.forculus deployof a container app whose build took more than about 15 minutes failed with “the push token was refused”, since the token was made before the build. It now asks for a new one when the build has left less than 10 minutes of it, and once more if the registry refuses it partway through the push.forculus deployrefuses an image whose programs are all for another architecture, as from aFROM --platform=linux/arm64line, before it’s pushed. BuildKit marks such an image as built for linux/amd64, so it went live and couldn’t run.forculus dockerfileand the MCP server’sgenerate_dockerfilesay a directory that deploys as a Worker or a static site isn’t a container app. They planned every directory as a container, so a Worker was told to write a Dockerfile, and a package.json Worker got a Node Dockerfile no deploy would use. Both take a kind (--kind,kind) to plan one as a container anyway.- A Bun project whose entry calls
Bun.serve, with no start script, is detected as a container that runs on Bun. Its TypeScript entry made it a Worker, which Cloudflare refused with “Bun is not defined”. A typed call likeBun.serve<{ user: string }>(...)counts too. A Worker with a Bun server beside it for local work, in a file package.json doesn’t name, stays a Worker. - An image
forculus deployrefuses before the push, for a key no deploy may carry, is sent to the platform to be kept. It’s listed byforculus scans, and the audit log, webhooks and Slack have anapp.deploy_blockedfor it, as for any refused deploy. The deploy printsScan: refusedand the scan’s id, as a Worker’s does. Nothing kept or logged the refusal. - The MCP server’s
set_envrefuses an argument it doesn’t take, likevaluesorenv, and names it and the ones it takes. It dropped them and said only that there was nothing to set.
0.1.12, 16 October 2026
Section titled “0.1.12, 16 October 2026”Added:
forculus deploy --instances <n>runs a container app on that many containers, each request going to one of them. forculus.json takes it ascontainer.instances.forculus scale <workspace>/<app> <n>changes the number without a deploy, andforculus app showgives it. The MCP server’sdeploytakesinstances.forculus workspace showsays how many containers an app may run on the plan, and the container units its container apps share.forculus workspace create --region eumakes a workspace that keeps its apps’ databases, files, containers and archived audit log in the EU, where we offer it. The EU isn’t open on forculuslabs.tech yet, so for now it’s refused. It can’t change later, and apps in an EU workspace need Enterprise. If the platform made it somewhere else, the command says so and fails.forculus workspace showsays where a workspace keeps its data, and the MCP server’screate_workspacetakesregion.
Changed:
forculus app showprints acontainers:line for a container app, betweendriver:andvisibility:.forculus workspace showprints adata kept in:line afterplan:, andcontainers per app:andcontainer units:lines with the plan’s limits.forculus auditdescribes a change to how many containers an app runs asnow runs 3 containers.- When forculus.json or a flag names a size or a number of containers,
forculus deployof a container app checks the workspace’s container units before it builds, and stops there when they’re spent, rather than after the push. container.instancesin forculus.json stops CLI 0.1.11 and older with an error that startsforculus.json isn't valid: unknown field `instances`. Upgrade every CLI that deploys the app before adding it, the GitHub deploy action’scli-versionincluded.- A container refusal past what Business runs now says to write to sales@forculuslabs.tech. CLI 0.1.11 and older add “Upgrade the workspace at” and the Billing link after it, which a Business workspace can’t do itself: an Enterprise contract is the way past Business.
0.1.11, 15 October 2026
Section titled “0.1.11, 15 October 2026”Added:
forculus preview end <workspace>/<app> --pull-request <n>ends the preview a pull request’s GitHub run put up. The deploy action runs it when the pull request closes, with the credential its run traded for. The MCP server has it asend_preview.forculus deploy --previewwith a pull request’s credential puts up that pull request’s preview, and says which pull request it’s for.forculus versionsandforculus auditname the pull request beside its previews.forculus policyshows and sets a workspace’s publish policy: who besides its owners may open an app to the whole workspace or an email domain, share outside the workspace, open public paths, make bypass tokens, mint agent credentials and connect AI clients.forculus policy breacheslists what was done before a rule by someone it doesn’t name, andforculus policy endends it. The MCP server haspublish_policy, which only reads.- Every deploy is scanned before it goes live, for keys left in what it sends and for packages
with published vulnerabilities.
forculus deploysends the packages its lock files pin (names and versions only) and the project’s.forculus-allow, reads a container image’s layers and config for keys before it pushes, and prints what the scan found under the app’s address. forculus scans <workspace>/<app>lists an app’s scans, refused deploys among them, and--version <n>or--id <id>prints one whole.forculus versionsmarks a version whose scan found something, andforculus auditsays what each scan found.forculus workspace scan-policy <workspace>shows whether deploys with findings warn or are refused, and--secrets blockor--vulnerabilities blockturns refusing on. Turning it off is done in the dashboard.- The MCP server has
deploy_scans, and a deploy’s answer says what its scan found and what to do about it. forculus log-drainsends an app’s logs, or every app’s in a workspace, to an HTTPS endpoint as signed JSON lines, Datadog, Grafana Loki or an S3-compatible bucket.testsends a test line,failedandretrylist and send again the batches that failed for a day, andpause,resume,levelandremovechange one. The MCP server haslist_log_drainsandset_log_drain, which pauses, resumes or changes the level of a drain but never adds one.forculus insights <workspace>shows who opens a workspace’s apps: people, opens and when each app was last opened, the apps nobody opened in 30, 60 or 90 days, and apps whose owners have all left.--appnarrows it to one app,--dayspicks 7, 30, 90 or 365, and--csv appsor--csv peopleprints a CSV. The MCP server has it asusage_insights, which only reads."kv": trueand"queue": {"path": "/jobs/queue"}in forculus.json give an app on Team or Business a key-value store and a queue of its own, made by the next deploy and bound asKVandQUEUE.forculus kv status,ls,get,putandrmreach its keys, andforculus queue statusandsendits queue. The MCP server hasapp_stores,list_kv_keys,get_kv_value,set_kv_value,delete_kv_keyandsend_to_queue.forculus db query <workspace>/<app> "<statement>"runs one statement on an app’s database and prints a page of the rows as a table. It’s read only unless you add--write, takes--limitand--offsetto page, and--csvprints up to 10,000 rows as CSV, or saves them with-o. The MCP server hasquery_database, which only reads, andlist_files, which lists an app’s files.- The MCP server has a tool for nearly everything the CLI does. An agent can now share
an app with a domain or a group, answer requests for access, look after groups, read members,
custom roles, single sign-on and SCIM, read and test webhooks and audit log streams, set an app’s
AI budget and outbound list, pause, start and run schedules, put a preview live, read and change
an app’s files and database, export and restore a database, restore a deleted workspace, read
storage and traffic, list connected AI clients and revoke an agent’s credential. Anything that
deletes or replaces data takes what it changes named twice, and so does turning an app’s
outbound list off.
query_databaseruns only a statement that reads. No tool changes who signs in, adds a member or a role, changes a group that gives a role, or sends the workspace’s events somewhere new. - The MCP server’s
show_workspacereads a workspace’s plan, limits and allowed email domains,list_cli_sessionslists your CLI sessions without where they signed in from, andgenerate_dockerfileshows the Dockerfile a container deploy would build with, writing nothing. forculus sso <workspace> --statusandforculus scim <workspace> --statusshow how a workspace signs in and whether its SCIM token works, for its owners. Changing either stays in the browser.forculus dev --astakes a persona as well as an email:owner,editor,viewer,agent,token,scheduler,webhook(with--providerand--pattern),outsiderandnobody. Each gets the token the platform would mint for that kind of caller, so an app’s checks for agents, bypass tokens, scheduled jobs and webhooks run locally. An outsider gets the platform’s 404 and never reaches the app, and a webhook reaches only the path its pattern opens. The page at/.forculushas a button for each.forculus dev --dockerruns a container app in the image a deploy would build, published on 127.0.0.1 only. A project that is only a Dockerfile runs this way without the flag.- A build for Windows on Arm. The PowerShell install script and the npm package install it on an arm64 PC, which until now got the x86-64 build and ran it under emulation.
forculus importbrings a project over from Vercel, Netlify or Replit, or from any git repository, as a new app:forculus import vercel:<project> acme/app,netlify:<site>,replitor the repository’s address. It reads the project’s settings and production variables with a token you paste, used for that import only and never saved, clones the code, writes a forculus.json, makes the app, sets its variables and secrets, deploys it and adds its scheduled calls. It shows all of that and asks first, and names what can’t come along with what to do instead.--dry-runonly shows it. The MCP server has it asimport_app, which never takes a token or a key from the conversation.forculus postgresconnects a Postgres database you already have, like Neon, Supabase or RDS, and gives it to apps, which query it through the platform and never hold its password.add,rotate,update,check,remove,grant,revoke,showandlist. The URL or the password is read at a hidden prompt or from stdin, never from a flag. Team connects up to 3 and Business up to 10. The MCP server haslist_postgres,check_postgres,grant_postgres,revoke_postgres,remove_postgresandapp_postgres. Connecting a database and changing its password stay yours, at the prompt.forculus networklets an Enterprise workspace’s apps reach services inside the company’s network, through a connector you run there, and lists the fixed addresses its apps send from.addprints the command that runs a network’s connector,service addputs a host and its ports on it, andgrantandrevokechoose which apps reach it.forculus ai key settakes keys for Google Gemini, Mistral, Groq, Cohere, Azure OpenAI (with--endpoint) and Amazon Bedrock (with--region), beside Anthropic and OpenAI.forculus env setrefuses a Groq or Bedrock key as a plain variable, as it does the others.FORCULUS_DEBUG=1prints the connection’s own error when a request gets no answer, for support.
Changed:
forculus devsetsFORCULUS_DEV=1for the app it runs. The SDKs from 0.3.0 take its http issuer only with it, and the platform never sets it.--roleis now only for an email, an agent or a token.--as viewerreplaces--as you@example.com --role viewer, which still works.- The
/.forculuspage and the outsider’s page can’t be framed by another site. - An app that takes the connection and closes it without answering, as a container does while it starts, gets a 502 that says so rather than an empty reply.
forculus dev --dockerremoves its container and image however it stops, an error included, and waits for the container to go before it removes the image.- A wrangler config that binds D1, KV or a queue under a name the platform doesn’t give is refused with everything else it asks for, in one message, rather than on its own.
forculus app deletewarns by the terms the app’s owners agreed to. A deleted app is kept for 30 days only when you and every owner of the app and its workspace have accepted the 5 October terms, or from 4 November, and is otherwise deleted for good as before. A delete you were told is kept goes through with--kept, and nothing is deleted if that no longer holds. With Recently deleted full, it says only--for-goodgoes through. If it can’t read the app, it says so rather than warning that the delete is final, and an app that isn’t there is said to be missing.forculus app delete --for-gooddeletes an app already in Recently deleted, and a deleted app says it can be deleted for good too.forculus workspace delete --for-goodopens the page that deletes a deleted workspace for good. The MCP server’sdelete_apptakesfor_goodandkept.- A first deploy that fails takes back the app it made, by its id, and deletes it for good, so the same deploy can run again. If the app changed in between, it’s left as it is and the deploy says why.
forculus deployto an app that was deleted says so and until when it can be restored, rather than offering--create. A deploy cut off by its workspace being deleted says it isn’t live, and what to delete after a restore. A directory given before the app is told the app goes first.forculus workspace membersmarks who waits for the identity provider after a restore, andforculus whoamisays where you’re held.forculus auditsays when someone held was let back in.forculus db restorethat may have run says the bookmark that undoes it. When the platform won’t say where a restore goes, having been asked too often that hour, it asks as it used to rather than saying only to wait, since--yesstill restores.forculus files rm,getandputkeep a leading./in a file’s name:rm ./secretdeletedsecret.getandputrefuse it, as any dot part.forculus files get --outwrites into a device or a pipe as it is, so/dev/nulland/dev/stdoutwork.- A request that gets no answer says plainly why: a proxy that refused it, nothing listening (on Windows too), or a request that couldn’t be made at all, which isn’t tried again.
- The line naming a request for support goes under a message of more than one line, not after it.
- The dashboard opens in a browser only for someone at a terminal, never under
--jsonor in a script. forculus app listsays1 personfor one person outside the workspace, not1 people.forculus accesson an app shared with nobody says so and who can still open it, where it printed nothing.- The MCP server asks again before
set_outbound_listturns an app’s list off, marksexport_databaseas one to ask about, since the database pauses while it runs, and names its own tools for the CLI’s commands in limits and refusals.
0.1.10, 6 October 2026
Section titled “0.1.10, 6 October 2026”Added:
forculus billing show <workspace>prints the plan, what it costs, when it renews and this month’s requests against the allowance. On Enterprise it prints the contract’s terms and the invoices Stripe sent, each with a link to pay it.forculus billing openopens the Billing tab, since paying and changing the plan stay in the browser. The MCP server has the same asbilling_status, which only reads.forculus sessions listandforculus sessions revoke <id>list and end your CLI sessions.forculus logout --everywhereends every one of them, this one too, and disconnects every AI client you connected. Bypass tokens and agent credentials you made keep working after you sign out: add--tokensto revoke them too.forculus connections listandforculus connections disconnectlist and disconnect the AI clients connected to apps as you, or to an app you manage.forculus agent renamerenames an agent. The MCP server has it asrename_agent.forculus webhook editchanges which events a webhook or Slack channel gets, or turns it on or off.forculus egress usage <workspace>/<app>shows the hosts an app sent requests to over the last 30 days, with what its list refused. The MCP server has it asoutbound_usage.forculus sso,forculus scimandforculus workspace exportopen the dashboard’s page for each. A CLI session can’t change who signs in to a workspace or carry it off.forculus app deletedandforculus app restorelist and restore the apps a workspace keeps for 30 days after they’re deleted, where the platform keeps deleted apps.forculus workspace deletedandforculus workspace restoredo the same for workspaces. The MCP server haslist_deleted_appsandrestore_app.forculus db restore --toputs an app’s database back as it was at a moment in the last 30 days, and prints a bookmark that undoes it.forculus rolemakes, changes and deletes a workspace’s custom roles, and gives them to members or groups.forculus workspace add --custom-rolegives one as the member joins.forculus audit-streamsends a workspace’s audit log to Splunk, Datadog, an HTTPS collector or an S3-compatible bucket, and retries failed batches. The MCP server haslist_audit_streams, which only reads.
Changed:
forculus app listshows owners and admins, under each app, who can open it, what it sends requests to, who made it and when it was last opened.forculus workspace showandforculus workspace usageknow Enterprise: what its plan allows, and its usage against what the contract includes each calendar month.forculus app deletesays whether the app can be restored and until when.--for-goodskips the 30 days.forculus deploytakes aworkerorcontainersection in forculus.json as the kind it names, and stops on a section for another kind than the one deployed, which it used to drop without a word. Anindex.htmlbeside a Worker’s entry is no longer taken for a static site: the deploy asks which you meant.- The rewrite printed for an old flat forculus.json keeps its
kind. Without it, a Worker with anindex.htmlwent up as a static site serving its own code. - A static site never uploads forculus.json or a wrangler config from its top, and stops on TypeScript source, which a site would only serve for anyone to read.
- A failure on our side names its request, for support to find it.
forculus curltakes the file the MCP server’screate_bypass_tokenwrites as it is, in--tokenorFORCULUS_TOKEN: its#lines are skipped. Anything else that isn’t one token is said plainly, not asbuilder error: failed to parse header value.forculus schedule listsays when a schedule last ran late, and how many runs that one stood in for.forculus rollbackto a preview says the version is live, not live again. So does the MCP server’srollback, which said the app was back on it.forculus secret setrefuses a host no app’s request reaches, ours or one under.internal,.localor.localhost, before it asks for the value.forculus db export --out -writes to stdout, not to a file named-.forculus files getdownloads a file whose name has a space or a%in it, which it could list but not fetch. A file that isn’t there isno_such_file, not a missing app, a static site says it keeps no files, and--out -writes to stdout.forculus files putnames the value that can’t be a file’s name, and says so when its two names are the wrong way round.forculus workspace domains <workspace>with no list shows the domains set now.forculus app listwith no workspace lists the apps of the only one you’re in.forculus whoamiandforculus workspace showsay when Free’s apps are paused because we can’t count their usage for a while, and that it’s ours to fix.forculus auditand the audit CSV mark what the identity provider did over SCIM, as the dashboard does.forculus ai key removeasks first, and takes--yeswhere there’s no terminal to ask at. AI budgets are printed with their thousands grouped.forculus deployfinds a Worker inworker.js,worker.mjsand theirsrc/copies, and in themainormodulepackage.json names when that file has a default export.forculus mcp install cursorfrom npm says the command it wrote is npm’s own copy, which runs without Node and stays put through updates. From npx, pnpm dlx or bunx it stops, since their cache is cleared.- Every failure under
--jsonis JSON with acode: a command line the CLI can’t read isinvalid_argument, and one that had none isfailed, notnull. - Prose is wrapped to 80 columns at a terminal. Piped or saved, it’s as before.
forculus app public-pathsprints, under each path, the address to send its requests to. The app’s own address asks everyone to sign in, public paths included, so a 401 there read as the path not being open. The MCP server’slist_public_pathshas it asurl.forculus token listsays when each token ends, or ended.forculus deployof an app that isn’t there, by someone whose role can’t make apps, says a workspace owner or admin has to make it. It offered--create, which was then refused.forculus webhook listsays why a webhook is off, and when one that’s still on is failing.forculus auditnames the webhook a webhook entry is about and a database restore’s undo bookmark, and names an agent’s request by the name it had when it made it.forculus curlprints only the error when an answer stops short, or a stream stops because your session ended, in place of the status. It printed200 OKfirst, over an error and an exit code of 1.forculus promoteof a version that failed keeps Cloudflare’s reason on one line, and the message ends with a full stop.forculus schedule offon a schedule that’s already off, andonon one that’s already on, say so and change nothing. One we turned off says why and that it has to be added again.forculus webhook testexits with 1 when the test event wasn’t delivered.forculus webhook add --nameandforculus webhook renamerefuse a name another webhook or Slack channel in the workspace already has, in any case.forculus workspace usagesizes its table the way its storage line, the plans and the dashboard do, in units of 1,024, so the same files no longer read 1.2 GB in one and 1.3 GB in the other. A database or files not measured yet saynot measured, not0 B.--helpends every help text of more than one sentence with a full stop.forculus deployto a name that can’t be one quotes it as you typed it. A name of lowercase letters alone came back with a capital.forculus share <app> @resend.devis refused: nobody has an inbox at a mail testing service’s domain, and a share with it reached every test account.
0.1.9, 5 October 2026
Section titled “0.1.9, 5 October 2026”Added:
forculus webhook add --namegives a webhook or Slack channel a name, like “deploys to ops”, so you can tell apart several that go to the same site.forculus webhook renamenames one later, or takes its name off, andforculus webhook listshows the name beside its address.forculus workspace usageshows what the workspace stores against its plan, split into site files, databases and app files, and each app’s files in the table. When the workspace is over, it says what that stops and how to make room.forculus ai grantandforculus ai showsay when an app’s list of hosts is on and leaves out the provider’s, so its requests to Anthropic or OpenAI would be refused, and give theforculus egress allowcommand that fixes it.forculus auditandforculus watchmark a request made to a preview, with the preview’s name, likep-0a1b2c3d. Before, only the host in--jsontold it apart from the live app.forculus access <app>ends withviewer everyone in acmewhen everyone in the workspace can open the app, and--jsonhasvisibility. It listed only the people it was shared with.
Changed:
forculus group deleteneeds--yes. Deleting a group takes away every app’s share with it, and the only way back is to make the group again and share each app again. Group messages call a group “the ops group” throughout.forculus deployof a static site checks its_headersand_redirectsbefore uploading anything. A rule the platform would refuse stopped the deploy only after the changed files had gone up and counted toward your storage.forculus promoteof a version that failed says it didn’t deploy and why. It said to runforculus rollback, which then refused it. A version that’s already live, or still going up, says so too.forculus deployto a workspace or app name that can’t be one, likeacme/My_App, stops straight away and says what a name can hold, before looking at your project. Yun looks sad only when the platform turns a deploy down, not over a typo or something that isn’t there.forculus files rmof a name the app doesn’t have says nothing was deleted, rather than “Deleted”.forculus files lssays its times are UTC.forculus webhook teston a Slack channel that Slack answered 404, 410 or 403 says what that means in Slack: the webhook or its channel is gone, the channel is archived, or Slack won’t let it post there.- Sharing an app you can’t see says there’s no such app you can see and how to list the ones you
can. It said only the app’s owners and admins could share it. Unsharing someone the app isn’t
shared with no longer starts “not found:”, and points to
forculus access <app>. forculus schedule addand the MCP server’sadd_schedulewith a shortcut like@dailysay shortcuts aren’t read and give the five fields to write instead. They said the schedule had one field.- The MCP server’s
rollback, used to put a preview live, says the app is now on that version, not back on it: a preview was never live. forculus agent createsays what to run next with the agent’s name, as the other agent commands do, rather than its id.forculus deploy --previewsays a preview answers until the app’s next preview, whoever puts it up, not until “your” next one.--helpfits an 80-column terminal. Each paragraph was one line, up to 241 columns, which the terminal broke again mid-word.forculus schedule onfor a schedule we turned off, because whoever added it can no longer edit the app or for single sign-on, says to add it again. It said the schedule would run, and the platform turned it off again a minute later.forculus auditsays whether a schedule was turned on or off.forculus auditnames the agent an app was granted to, or a credential was minted for or revoked from, asrelease-bot (ag_…), the way it names an agent it made or ended.forculus auditsays the role a member or a grant had before a change, asbo@example.com as admin, was owner, and which limit aplan.limitentry hit, asaudit_export, not on free.forculus group addsays when the person is already in the group, rather than that it added them, and names the group as “the finance group”.forculus deployof an app that isn’t there, without--create, gives the command with--createthat makes it.forculus curlwrites the body out as it arrives, so an event stream shows each event as it’s sent. An answer that stops short, or a stream that stops because your session ended, is an error that says so. It printed200 OKand exited 0.
0.1.8, 3 October 2026
Section titled “0.1.8, 3 October 2026”Added:
forculus share <app> @acme.comshares an app with everyone whose address is exactly at that domain, as viewers. You can share only the domain your own address is at, and never a free mail provider’s or a public suffix.forculus share <app> --group <name>shares an app with one of the workspace’s groups, as viewers.forculus groupcreates, lists and deletes groups and adds and removes their people, who needn’t be in the workspace.forculus unsharetakes@<domain>and--groupthe same way. When someone you unshare can still open the app through their domain, a group or the whole workspace, it says which.forculus access <app>lists the domains and groups an app is shared with, after the people.forculus workspace removesays which apps the person’s domain still lets them open.forculus mcp’sapp_statusshows the domains and groups an app is shared with, andset_accessnames what still lets someone in after their access is removed.forculus domain add,listandremovegive an app a custom domain, likeapp.example.com.addprints the records to add at your DNS provider: a CNAME that points the name at us, and a TXT record that proves it’s yours. The domain works once we’ve seen both and its certificate is issued. The MCP server has the same asadd_domain,list_domainsandremove_domain.forculus agent create|list|grant|revoke|token|end: agents are callers of a workspace’s own, each reaching only the apps granted to it withfca_credentials of its own, and named as itself in the audit log.--jsonon each.- The MCP server has
list_agents,create_agent,set_agent_accessandend_agent. None of them mints a credential, so a secret never passes through an agent’s conversation. forculus audit,watchandtopname an agent asagent:<id>.forculus egress list|allow|deny|on|off <workspace>/<app>holds an app to a list of hosts it may send requests to.allowtakes a host likeapi.stripe.comor a pattern like"*.stripe.com". Only workspace owners and admins can add a host or turn the list off.forculus aishares the workspace’s Anthropic or OpenAI key with apps, each up to a monthly budget:forculus ai key set <workspace> anthropicstores it (pasted at a hidden prompt or piped in),forculus ai grant <workspace>/<app> anthropic --budget 25lets an app use it, andforculus ai keys,showandrevokelist, show and take it away.forculus webhook add|list|remove|testsends a workspace’s events to a URL of yours, signed in theForculus-Signatureheader, or with--slackto a Slack channel, whose URL you paste at the prompt or pipe in so it stays out of your shell history.addshows the signing secret once, andtestsends a test event now and says how it went. For owners and admins.forculus deploy --previewputs a Worker app or a static site up at a preview address of its own,p-<eight characters>--<app>, and leaves the live version as it is. Only people the app is shared with can open it, for 7 days or until the next preview.forculus versionsshows each preview’s address and whether it still answers.forculus promote <app> <version>puts a preview live, as it is.forculus schedule add|list|remove|runcalls a path on an app on a schedule, as five cron fields in UTC. Each run is signed as the scheduler:kindservice, with asubthat starts withscheduler:.forculus schedule onandoffturn a schedule on and off. One we turned off because its maker can no longer edit the app, or for single sign-on, is added again instead.- The MCP server’s
deploy_apptakespreview: true, andlist_schedules,add_scheduleandremove_schedulemanage an app’s schedules. forculus files ls|get|put|rm <workspace>/<app>lists, downloads, uploads and deletes the files an app keeps, where the platform has storage on. Owners can do all four, admins can list and delete.getsaves under the file’s own name, or--out, or--stdout, and won’t replace a file that’s already there unless you pass--force.puttakes up to 25 MB, and-reads stdin. Forgetandput, a name with anything that would need encoding is refused before anything is sent.rmtakes any name the app wrote, spaces and all, so you can always make room. Only a name with a.or..part can’t be deleted this way.lsshows control characters and anything else outside plain ASCII in a name escaped, so a name an app wrote can’t act on your terminal.forculus deploytakes a deploy credential fromFORCULUS_DEPLOY_TOKENand uses it in place of a login. A GitHub Actions run gets one by trading its OIDC token, which the deploy action inactions/deploydoes for you, so nothing is stored in the repository. The credential deploys the one app it was made for and nothing else, so--createisn’t taken with it. Nothing the CLI starts (your build, wrangler, docker) getsFORCULUS_DEPLOY_TOKENin its environment, so a build can’t bake it into the app.FORCULUS_DEPLOY_TOKEN_FILEnames a file holding it instead, which the deploy reads and deletes before it starts anything, so the credential is in no process’s environment at all. The GitHub action hands it over that way. A file that doesn’t hold a deploy credential, or is a link, is left alone and the deploy stops.forculus versionsnames the GitHub Actions run that made a version, beside the person whose permissions it had.forculus app mcp <workspace>/<app> onlets AI clients such as Claude and Cursor connect to an app that is an MCP server. Each signs in as someone the app is shared with, who approves it in a browser, and reaches only that app, as them.offdisconnects every one of them, from its next request, and turning it on again brings none back.forculus app showsays when AI clients can connect.forculus access <app>ends with how many people are waiting for access, when you can share the app and anyone is, and points toforculus access <app> requests.forculus mcp install cursorputsFORCULUS_CONFIG_DIRin the server’senvwhen the config directory isn’t the default one, from the environment or--config-dir, and says which. Cursor doesn’t have your terminal’s environment, so the server it started read the default config’s session instead. That directory holds the platform you signed in to, soFORCULUS_APIisn’t written.- Yun, our cloud, says hi over
forculus --helpand a bareforculus, after you sign in, afterforculus initwrites a template and at an app’s first deploy. Yun naps when you sign out or nobody is signed in, and looks a little sad when the platform turns a deploy down. Yun goes to stderr, and only when stdout and stderr are both a terminal: never with--json,NO_COLORorCIset.FORCULUS_NO_MASCOT=1turns Yun off. The MCP server never draws Yun.
Changed:
forculus deployends with “Version 3 (…) is live.” and the app’s address, rather than “Deployed version 3 (…).”. For an app that’s taken down it says the version is saved, but not served until the takedown is lifted.forculus share,forculus access approveand the MCP server’sset_accesssay someone can now use the app “as a viewer” or “as an editor”, rather than “as viewer”.forculus deploy --dry-runends with “That was a dry run, so nothing was deployed.”, and signing in with no workspace yet says to make one, a little shorter.forculus access <app> --jsonnow answers{"grants": [...], "domains": [...], "groups": [...]}rather than a bare list of people, so the domains and groups an app is shared with show too. People are undergrants, as before.- In
forculus audit --json,app.database_execandapp.database_exec_refusedentries from 3 October 2026 on have a keyed hash of the statement insha256, and say which key made it inhash, like"hmac-v1:k1". Before,sha256was a plain SHA-256 of the statement, with nohash, so an old entry never matches a new one, and hashing a statement yourself won’t find it. Entries whosehashdiffers never match either. - A
forculus deploywhose connection drops before the platform answers looks for the new version, as it does for a deploy that takes too long, instead of failing with what the TLS library said. - A deploy that made its app and then failed deletes the app again only once the platform says
none of its versions is live or on its way. When the deploy may have gone through, or nobody can
tell, the app is kept and the error says so, with
forculus versionsto see whether it went live. That error is one sentence for a deploy that timed out or that a gateway gave up on too. It said where to look twice, with the connection’s own error in between. A deploy that failed before it sent its version, uploading files or pushing an image, deletes the app straight away as before: nothing that could go live had left your computer. - A deploy whose connection drops stops looking for its version after 20 seconds when none has turned up, rather than 3 minutes: a drop can come before the deploy reached the platform.
- A deploy that made its app no longer tries to delete it after a failure when the app is already gone, or out of your reach. The error said to delete an app that wasn’t there. One out of your reach is still there, and the error says so.
- A version on its way live is looked at every second for its first 15 seconds, then every 5, so a deploy that goes live a moment after the platform answers says so a moment later.
forculus rollback,forculus env setandforculus env removewhose connection drops, or that the platform doesn’t answer in time, say the change may have gone through and how to check, instead of what the TLS library said. So do the agent’srollbackandset_env.forculus access approve <app> <id>anddeny <app> <id>for a request that’s already decided, or was never there, say so, or that there’s no open request with that id, and how to list the open ones (forculus access requests <app>). They said the app wasn’t found and offered to create it.denynames the person it turned down, not the request’s id.forculus deploy --createthat can’t delete the app it made after a failed deploy no longer puts a full stop after the platform’s reason when it already ends in one, a?, a!or an ellipsis, inside closing quotes or brackets or not. A reason ending in a colon gets a full stop in its place, one ending in a link is followed by a new line rather than running on into how to delete the app, and no reason at all leaves no stray stop.forculus deploysays “Deploying 1 file”, not “1 files”, and a workspace at a plan’s limit of one app “has 1 app”.forculus initnames your workspace in the deploy command it ends with when you’re signed in and in only one. Otherwise it still says<workspace>.forculus audit --csvprints the oldest entry first, the same order as the dashboard’s download.forculus auditwithout--app, refused to an app’s owner, says to try--appwith the app’s name: an owner can read the log of the apps they own.forculus watch <workspace>says to watch<workspace>/<app>, and the MCP server’saudit_logsays to passapp.- The
static-sitetemplate fromforculus inithas a404.html. A path with no page gets it, with a 404 status, instead of the front page with a 200. forculus curlsays so when a redirect came back with noLocation. The gateway now drops every redirect that would leave the app’s own/<workspace>/<app>, to another site too, so a token never follows one onto another app. An app that hands a script a URL somewhere else, like a signed download link, returns it in the body instead.- Taking away something that isn’t there says what’s missing rather than that the app or the workspace is: sharing with a group that doesn’t exist, unsharing a group or a domain the app isn’t shared with, removing a custom domain, a schedule or an outbound host the app doesn’t have, adding to or removing from a group, revoking an agent’s credential that’s already revoked, and taking an app from an agent that never had it.
forculus agent endasks before it ends an agent for good, and with no terminal to ask at needs--yes. The agent commands call an agent by its name, as you typed it, and say “credentials”, not “tokens”.- A viewer’s
forculus deploy, or--preview, says who can deploy the app before it detects anything. Granting an app to an agent without the role for it says who can. forculus deploy --json --previewputs the preview’s address inurland the live app’s inlive_url, the way the MCP server’sdeploy_appdoes.forculus promoteof a preview pushed out of the newest 20 versions says it’s too old to promote.forculus auditnames an agent and the credential it used, the scheduler by its schedule, and whose request for access was approved or turned down.forculus schedule listsaysoff (maker_cannot_edit)for a schedule turned off because its maker can no longer edit the app, andoff (sso_required)for one single sign-on turned off.forculus access requestskeeps the line breaks in a note.forculus share <app> *.acme.comsays it isn’t a domain, andforculus share <app> acme.comsays to put@in front. Both said it wasn’t a valid email address. The usage line ofshareandunshareshows--group, andunsharesays the address as the platform keeps it.forculus curlsays a refusal came from Forculus rather than “our edge”, and names your workspace in what to run next.forculus webhook add --slacknames it too, not<workspace>.forculus ai showsays “1 request”, not “1 requests”.
Security:
- With
FORCULUS_DEPLOY_TOKENset,forculus deploysends it to the APIFORCULUS_APInames, or to https://api.forculuslabs.tech, never to the API this machine is signed in to. - Nothing the CLI starts gets
ACTIONS_ID_TOKEN_REQUEST_TOKENorACTIONS_ID_TOKEN_REQUEST_URL, so a build’s dependencies can’t ask GitHub for a token of their own in a deploy job. - A deploy now stops on a file that holds one of our own tokens (a deploy credential, a bypass token, a session or a push token), not only a CLI session.
Upgrading:
- On a network, or behind a proxy, that cuts a request after 5 seconds with nothing back, the platform now answers a deploy from 0.1.2 or later in time, and the CLI follows the rest. 0.1.0 and 0.1.1 still wait in the request for a first site to go live, so there they can lose the connection and delete the app they made a moment before. Upgrade if you’re on either.
0.1.7, 2 October 2026
Section titled “0.1.7, 2 October 2026”Added:
forculus uninstalltakes the CLI off your computer. It revokes the bypass tokens your agent made here and deletes their secret files, ends the session, and deletes the config directory, leaving anything in it the CLI didn’t write. If the install script putforculusthere, it deletes the program and takes out the lines the script added to your shell’s startup files, and nothing else. For Homebrew, npm, cargo and Windows it prints what to run. It takesforculusout of the project’s.cursor/mcp.jsonand says how to take it out of other agents. It asks first, or takes--yes.--keep-configkeeps your settings. Your apps and workspaces aren’t touched.forculus init <template> [dir]starts an app from a template, ready to deploy:static-site,worker,internal-tool(a Worker with a database),expressorfastapi. It writes into./<template>unless you name a directory, never replaces a file that’s already there, and ends with theforculus deploycommand to run next.forculus initon its own lists the templates.forculus mcphaslist_templatesandcreate_from_template, so an agent can go from a sentence to a deployed app. A template is written only where a deploy could read from, by the same rules asdeploy_app, and never over a file. Neither needs anyone signed in.forculus access <app> requestslists the open requests for access to an app, with what each person wrote.forculus access <app> approve <id>shares the app with them as a viewer and emails them its link.denyturns a request down without telling them.forculus audit --csvprints the log as CSV, with the same columns as the dashboard’s download. A cell that a spreadsheet would read as a formula is written so it reads as text.
0.1.6, 1 October 2026
Section titled “0.1.6, 1 October 2026”Added:
forculus mcp install cursorsets Cursor up for the project you run it in. It writes the project’s.cursor/mcp.jsonwith thisforculus’s full path, so Cursor finds it even when it was opened from the Dock, which doesn’t have your terminal’sPATH. Running it again changes nothing. Other servers in the file are kept, and a file that isn’t valid JSON is left alone.
0.1.5, 1 October 2026
Section titled “0.1.5, 1 October 2026”Changed:
- Every error reads as a sentence. The CLI’s own words start with a capital, every message ends with a full stop, and a name at the start, like a workspace’s, is never capitalised.
forculus deployandforculus detectno longer takefromin a string, a comment or a regular expression for an import. A Worker with"comes from"in it was refused for a package called+that it never imported.forculus logs --version 3finds version 3’s lines when version 4 wrote a lot in its first minutes, and refuses a version the app never had. A read with no lines says “in the last hour”, not “since 1h”, and how far back the plan lets you read, not how long it keeps lines: every plan’s lines are kept 7 days. A viewer is told once who can read the logs.forculus app showlists the paths anyone can reach without signing in, and which of them have no signature check.--jsonhas them aspublic_paths.forculus auditputs(deleted)after a deleted app’s name on every entry about it, andforculus watchdoes on requests too. An env removal readsremoved, notunset, and an allowed request readsaccess allow, without(allowed).forculus curlsays plainly that a 401 or 403 without our edge’sX-Forculus-Fromheader came from your app.forculus db exportsays “Exported the database of acme/notes to acme-notes.sql (48,213 bytes)”, with the size grouped by thousands.forculus workspace usagesays storage over the limit in GB-months, the unit it’s priced in, rounded as the Billing tab rounds it. A meter we haven’t priced yet says that’s ours to fix, the usage limit rule is said once in plain words, and requests with no CPU measured say only their CPU time is free. A limit lowered partway through an hour is said to still count that hour against the higher one, and apps paused at the default $0 limit are said to have run out of the plan’s allowances, not its usage.forculus workspace usageon a subscription made at an older price says it keeps the prices it started with, usage prices included, until the workspace changes plan. It showed $199 with nothing to say why the pricing page says $215.forculus workspace usage --previousfor a short time on Free before a plan began says when it was, to the minute, instead of calling it last month. An empty table says no app used anything, not that the workspace has no apps.--drivertakesworkerandcontaineras well, and its help says which kinds each runs.forculus detectcalls a JavaScript monorepo’s root that, not a workspace.
Agents:
- Refusals read as sentences, with a capital and a full stop, and never capitalise a workspace’s name.
set_visibilitysays who can reach the app, as the CLI does.call_app,send_to_appandset_envtake headers and variables as an object of names and values too, and any other shape is refused with both shapes shown.app_logsrefuses a version the app never had, finds a version’s lines behind the next one’s, and says when a short page has older lines to read on to. Someone who can’t read the logs is told who can, asforculus logssays it, not the platform’s “You don’t have permission to do that.”app_statuslists the paths anyone can reach without signing in.call_appsays a 401 or 403 without our edge’s mark came from the app.workspace_usagesays storage in GB-months and names a short time on Free before a plan as that. It says a subscription made at an older price keeps it, and words the usage limit as the CLI does.
0.1.4, 1 October 2026
Section titled “0.1.4, 1 October 2026”Changed:
- After a deploy or rollback that replaces a live Worker version,
forculussays the old version may answer some requests for a minute or two, not up to a minute.env setandenv removesay the same of when the live code sees the change. forculus logslabels each line with the version that wrote it, not the one that was live, so--versionshows the lines that version wrote. A line we can’t tell, such as a container’s own output, still gets the version that was live when it was written.forculus app liston an empty workspace tells an owner or admin it has no apps yet, and tells a member there may be apps not shared with them.forculus rollbackto a version the app doesn’t have says the app has no such version, and points atforculus versions.forculus workspace usageshows the default usage limit as$0 a month, as the dashboard does, and says whether usage before the billing month began in its first hour was on Free, and so on no invoice, or on the last month’s invoice.
Agents:
rollback,deploy_appandset_envsay a replaced version may answer for a minute or two.app_logsgives each line the version that wrote it, and itsversionfilter means lines that version wrote.rollbackto a missing version says the app has no such version.workspace_usagesays the default usage limit is$0 a month, and whether usage in the billing month’s first hour was on Free or on the last month’s invoice.
0.1.3, 1 October 2026
Section titled “0.1.3, 1 October 2026”Changed:
- A container deploy removes the image it built from Docker however it ends: pushed, failed, or stopped with Ctrl-C. It says when the image is up and it’s waiting for the platform.
- Ctrl-C before a deploy’s version is sent says nothing was deployed, deletes an app
--createmade for it, and exits 130. forculus detect,forculus dockerfileanddeploy --dry-runno longer say a container has a Dockerfile when it has none. The dry run refuses what the deploy would, anddetectexits 1 and gives the reason in--jsonasproblem.forculus --json logslists lines oldest first, like the text.--followlooks back a minute, since Cloudflare can take half of one to make a line readable.- Not being logged in has a
--jsoncode:not_logged_in. forculus secret setchecks the name,--host,--headerand--formatbefore it asks for the value, and names the flag that’s wrong. It tells a header that isn’t one from one that can’t carry a secret, suggests the hostname for a host with a port, and gives these errors the--jsoncodeinvalid_argument.forculus rollbackto the version already live says so.forculus token listandforculus app listline up their columns, a small site’s size is<1 KiB, andforculus unsharesays plainly when the app isn’t shared with someone.- An app the platform took down says so in
app show,app list,deployandenv set: a deploy is kept but not served until it’s lifted. A deploy says why once, before it builds. forculus token revokeandforculus app deletedelete the files the MCP’screate_bypass_tokenwrote the tokens’ secrets to, as the MCP’s own tools do. Only files for the platform you’re signed in to go.- A workspace member at the plan’s app limit is told to ask an owner or admin, with no billing link, as the platform now tells them.
- An app that isn’t found says it may not be shared with you, and offers
--createonly when no app has the name. forculus audit --appfinds a name’s deleted apps too, and marks them(deleted).forculus workspace usageshows what a plan switch holds apart from the old plan, and--previouson a workspace made after that period says it has nothing to show.
Agents:
revoke_bypass_tokenanddelete_appdelete the filescreate_bypass_tokenwrote the tokens’ secrets to.create_bypass_tokenwrites no file for an id that isn’t a token id, and revokes that token.- A missing app points at
deploy_appwithcreate_if_missing,list_appsandwhoami, anddelete_appon one says it may be deleted already.set_accesssays once that someone has to join the workspace, andcall_appanswers every platform 404 as an error. - A refusal starts with a capital, and one a member can’t act on says to tell the person rather than offer an upgrade.
0.1.2, 30 September 2026
Section titled “0.1.2, 30 September 2026”Security:
- Names can’t hide characters any more.
forculusrefuses bidi controls, zero-width and every other invisible character in workspace, app and token names, and prints any stored before without them. A zero-width joiner stays only where it changes what shows, as in an emoji or a script that joins its letters, so one name can’t pass for another. The England, Scotland and Wales flags are allowed.forculus auditandforculus watchstrip control and bidi characters from what they print. - An email address with a hidden character in it is shown with that character escaped, like
ceo\u{200b}@acme.com, wherever the CLI and the MCP tools print one, so it can’t pass for another address. - A bypass token given to
forculus curlgoes only to the apps of the platformFORCULUS_APInames, or the one you’re signed in to. Before, with no login it went to production’s apps whateverFORCULUS_APIsaid. When the CLI doesn’t know where that platform’s apps are, it doesn’t send the token and says to set--apps-url. - An app that isn’t shared with you answers 404, the same as no app, so a refusal no longer tells you it exists.
forculus auditandforculus watchshow a refused or unknown bearer token as an unknown token, never as the platform.forculus curlrefuses gateway paths that aren’t UTF-8, and NUL.
New:
forculus workspace usageshows this period’s requests, CPU time, database rows and storage, what they cost past the plan, and where Free stops.--previousshows the last period. The MCP server has the same as the read-onlyworkspace_usagetool.forculus workspace usagesays your plan and what it costs a month, and when a switch to another plan is still going through. Apps deleted in the period stay in its table, marked(deleted). When a period began partway through an hour, it says why the apps can add up to more than the meters.forculus workspace usageandforculus whoamisay when a Free workspace’s apps are paused for the month.forculus logsleaves the platform’s own lines out unless you pass--platform, and reads a container’s log levels.forculus watchnames the app on each line.forculus loginsays what to do when the sign-in page emails you a code and it doesn’t come: check spam, ask for a new one, then write to support@forculuslabs.tech.- When the platform refuses you an app in a workspace you own or run,
forculus curlandcall_appsay the app is there and give theforculus sharecommand that lets you in. - Deploys: bun and deno apps, VitePress sites built from the root their script builds, uv and poetry apps run from where their packages went, and chained start commands. A deploy that can’t build or start is refused before it uploads, and a failed docker build shows the step’s output.
Changed:
- A deploy follows its version until it’s live and says which version it replaced. If Ctrl-C
stops the wait, it says whether the version went live, failed or is still going.
deploy_appnow says too that the old version can answer for a moment longer. - A container deploy removes the image it built from Docker once it’s pushed. A failed push leaves it.
- Python images install packages before copying your code, so an edit no longer reinstalls
everything and pushes every layer again. A
requirements.txtthat installs local wheels, or uses--find-linksor--no-index, builds. forculus auditnames deleted apps on every entry, and--apptakes a deleted app’s name. It names the recipient, the statement and the rollback, says whose version the platform put back and which, and what a refused removal would have taken.forculus watch --deniesandforculus topask the platform for refusals alone, so a busy workspace’s refusals no longer go missing. Neither skips entries that reached the log together.forculus watch --tail Ncounts only the lines it prints, and--tail 0prints none.forculus curlstarts the status on its own line when stdout and stderr share a pipe, on Windows too.- A refusal from the platform says so, with its cause. A 502 after dispatch no longer blames the app. A 404 says the app may not be shared with you, and who to ask.
forculus env seton a static site says the change comes with its next deploy.- A deploy refused for the plan’s app count suggests an upgrade only to a plan with room.
create_bypass_tokentakes any token name the platform takes, such asCafé ci.forculus token listsays a token opens nothing once its maker no longer has an account.- The audit log’s archive paging is followed in
forculus auditand the MCP tools.
0.1.1, 25 September 2026
Section titled “0.1.1, 25 September 2026”Security:
forculus curland the MCP toolscall_appandsend_to_apprefuse any path that climbs out of the app they name (..,%2e%2e,..%2fand the other spellings). Before, one app’s path could reach another app you can open.forculus mcprefuses to deploy from a hidden directory anywhere in the path, the project root included.
New:
forculus logs, and theapp_logstool, read an app’s recent output.forculus db execruns one statement on an app’s database.forculus app list --sharedlists apps people have shared with you.list_appsincludes them.- Container redeploys roll out instead of only patching the running container.
Agents:
deploy_appanswers within 45 seconds, or thewait_secondsit’s given, up to 240. A deploy still going carries on in the server, and the newdeploy_statuswaits for it. Codex stops a call after five minutes, and a plugin can’t give it longer.- Started in a plugin’s own directory, as Codex and every Agent Plugins client start it,
forculus mcpnever deploys from there. It goes byPWD, the directory the client was started from, and otherwise says to setFORCULUS_MCP_ROOTS.
Deploys:
- A plain Worker can import npm packages. It’s bundled with wrangler, like TypeScript.
- A static site’s own
404.htmlis served for paths with no file. - SvelteKit and Astro apps no longer fail on Cloudflare’s disabled default cache. A SvelteKit app without the Cloudflare adapter is told which adapter to add.
- Go and Rust images carry the project’s files beside the binary. Go builds use Go 1.27 and fetch a newer toolchain when go.mod asks for one.
- Generated container images run under tini, so they stop cleanly.
- A failed Docker build shows Docker’s last lines. An app that
--createmade is deleted again when its deploy fails. - The wrangler config is checked before the build, and the plan’s app count before a
--createbuild.
Smaller:
- Tokens default to 30 days, the most a CLI session gives them.
token listsays when a token’s maker caps what it can do. forculus auditlines say who did what to which app.- Clearer messages for env changes, ports, credentials in a build, and the terms to accept.
- MCP tools have titles, name each other rather than CLI commands, and mark app output as data in structured results too.
0.1.0, 25 September 2026
Section titled “0.1.0, 25 September 2026”First release, so there is nothing to upgrade from.
The Node.js SDK
Section titled “The Node.js SDK”@forculus/sdk on npm. Its docs.
0.3.1, 19 October 2026
Section titled “0.3.1, 19 October 2026”requireUserandverifyIdentityrefuse a token whoseaudis a list, even one holding the app’s id. The edge writes one string, andrequireWebhookalready refused a list.
0.3.0, 16 October 2026
Section titled “0.3.0, 16 October 2026”- An issuer or key set on http is refused unless
env.FORCULUS_DEVis"1", and even then only on this machine (127.0.0.1,localhostor[::1], and for the keyshost.docker.internaltoo).forculus devsets it for the app it runs, from CLI 0.1.11. The platform’s issuer and keys are https and it never setsFORCULUS_DEV, so a deployed app that still points atforculus devrefuses everyone, and asks nobody for keys. envFromProcess()readsFORCULUS_DEVwith the rest.- An app that checked tokens against keys of its own over http, outside
forculus dev, is now refused. That is why this is 0.3.0.
0.2.0, 6 October 2026
Section titled “0.2.0, 6 October 2026”- A caller has
agentName: the name of the workspace’s agent that made the request, ornullfor a person, a bypass token or a scheduled job.isAgent(user)says whether an agent called. Decide what an agent may do bysub, which isagent:<agent id>. The name can change. - An agent’s identity token with no name, an empty one, or an empty agent id is refused, as a person’s with no email is. The platform always sends one.
Userhas a new required field, so code that builds aUserby hand (in tests, say) needsagentName: null. That is why this is 0.2.0.
0.1.3, 1 October 2026
Section titled “0.1.3, 1 October 2026”- The README is rewritten in plain words. Nothing in the SDK’s behaviour changed.
0.1.2, 25 September 2026
Section titled “0.1.2, 25 September 2026”- The Express adapter’s 401 and 403 carry
Cache-Control: no-store, astoResponse()already did. - A 403 says
forbidden: this needs editoras plain text, the same body every SDK answers, rather thanforbidden.
0.1.1, 25 September 2026
Section titled “0.1.1, 25 September 2026”The first version on npm. 0.1.0 was tagged but its publish failed, so it was never released. The code is the same as 0.1.0 below.
0.1.0, 25 September 2026
Section titled “0.1.0, 25 September 2026”First release, so there is nothing to upgrade from.
requireUserandverifyIdentitycheck the identity token the platform sends with a person’s or a bypass token’s request, with Express, Hono, Next andnode:httpadapters.requireWebhookandverifyWebhookcheck the token it sends with a webhook delivery whose signature it verified, and name the provider. The adapters arerequireWebhookfor Express and Next,webhookandgetWebhookfor Hono, andrequireWebhookFromfor Node’s own headers. Each kind of token is refused where the other is expected.
The Python SDK
Section titled “The Python SDK”forculus on PyPI. Its docs.
0.3.1, 19 October 2026
Section titled “0.3.1, 19 October 2026”require_userandverify_identityrefuse a token whoseaudis a list, even one holding the app’s id. The edge writes one string, andrequire_webhookalready refused a list.- A request with two
X-Forculus-Identityheaders is refused, as the Node SDK refuses it. It read the first.
0.3.0, 16 October 2026
Section titled “0.3.0, 16 October 2026”- An issuer or key set on http is refused unless
FORCULUS_DEVis1, and even then only on this machine (127.0.0.1,localhostor[::1], and for the keyshost.docker.internaltoo).forculus devsets it for the app it runs, from CLI 0.1.11. The platform’s issuer and keys are https and it never setsFORCULUS_DEV, so a deployed app that still points atforculus devrefuses everyone, and asks nobody for keys. Confighasdev, whichConfig.from_envreads fromFORCULUS_DEV. AConfigwith an http issuer or key set raisesUnauthorizedwhen it’s made, unlessdevis true. That is why this is 0.3.0.- A
Configmade with an empty app id, issuer or key set URL raisesUnauthorizedsaying they must be set, asConfig.from_envdoes, rather than that they must use https.
0.2.0, 6 October 2026
Section titled “0.2.0, 6 October 2026”Userhasagent_name: the name of the workspace’s agent that made the request, orNonefor a person, a bypass token or a scheduled job.user.is_agentsays whether an agent called. Decide what an agent may do bysub, which isagent:<agent id>. The name can change.- An agent’s identity token with no name, an empty one, or an empty agent id is refused, as a person’s with no email is. The platform always sends one.
agent_namedefaults toNone, so code that builds aUserby hand keeps working.
0.1.2, 1 October 2026
Section titled “0.1.2, 1 October 2026”- The README is rewritten in plain words. Nothing in the SDK’s behaviour changed.
0.1.1, 25 September 2026
Section titled “0.1.1, 25 September 2026”- A 403 says
forbidden: this needs editoras plain text, the same body every SDK answers: Flask and Django saidforbidden: editoras HTML. FastAPI’s is that text as its JSONdetail.
0.1.0, 25 September 2026
Section titled “0.1.0, 25 September 2026”First release, so there is nothing to upgrade from.
require_userandverify_identitycheck the identity token the platform sends with a person’s or a bypass token’s request, with Flask, FastAPI, Django, Streamlit and Gradio integrations.require_webhookandverify_webhookcheck the token it sends with a webhook delivery whose signature it verified, and name the provider. The integrations arewebhook_requiredandget_webhookfor Flask, thewebhookdependency for FastAPI, andwebhook_requiredfor Django, which exempts its view from the CSRF check.forculus.aiohas both calls. Each kind of token is refused where the other is expected.
The Go SDK
Section titled “The Go SDK”the forculuslabs.tech/sdk module. Its docs.
0.3.1, 19 October 2026
Section titled “0.3.1, 19 October 2026”RequireUserandVerifyrefuse a token whoseaudis a list, even one holding the app’s id. The edge writes one string, andRequireWebhookalready refused a list.- A request with two
X-Forculus-Identityheaders is refused, as the Node SDK refuses it. It read the first.
0.3.0, 16 October 2026
Section titled “0.3.0, 16 October 2026”- An issuer or key set on http is refused unless
FORCULUS_DEVis1, and even then only on this machine (127.0.0.1,localhostor[::1], and for the keyshost.docker.internaltoo).forculus devsets it for the app it runs, from CLI 0.1.11. The platform’s issuer and keys are https and it never setsFORCULUS_DEV, so a deployed app that still points atforculus devrefuses everyone, and asks nobody for keys. ConfighasDev, whichConfigFromEnvreads fromFORCULUS_DEV.NewVerifierreturns an error for an http issuer or key set unlessDevis true. That is why this is 0.3.0.
0.2.0, 6 October 2026
Section titled “0.2.0, 6 October 2026”UserhasAgentName: the name of the workspace’s agent that made the request, or empty for a person, a bypass token or a scheduled job.user.IsAgent()says whether an agent called. Decide what an agent may do bySub, which isagent:<agent id>. The name can change.Usermarshalsagent_nametoo,nullwhen empty, as the other SDKs write it.- An agent’s identity token with no name, an empty one, or an empty agent id is refused, as a person’s with no email is. The platform always sends one.
- A
Userwritten as an unkeyed literal (User{"user", ...}) no longer compiles. Name the fields.
0.1.2, 1 October 2026
Section titled “0.1.2, 1 October 2026”- The README is rewritten in plain words. Nothing in the SDK’s behaviour changed.
0.1.1, 25 September 2026
Section titled “0.1.1, 25 September 2026”Requiresanswers its 403,forbidden: this needs editor, with no newline after it, the same body every SDK answers.
0.1.0, 25 September 2026
Section titled “0.1.0, 25 September 2026”First release, so there is nothing to upgrade from.
RequireUser,Verify,MiddlewareandRequirescheck the identity token the platform sends with a person’s or a bypass token’s request.RequireWebhook,VerifyWebhookandWebhookMiddleware(withWebhookFrom) check the token it sends with a webhook delivery whose signature it verified, and name the provider. Each kind of token is refused where the other is expected.UserandWebhookmarshal to JSON with the names the Python and Rust SDKs use:kind,sub,email,roleandworkspace_id, andprovider,pattern,delivery,signed_atandworkspace_id. An empty email or delivery id isnull, andsigned_atis unix seconds ornull.
The Rust SDK
Section titled “The Rust SDK”the forculus crate. Its docs.
0.3.1, 19 October 2026
Section titled “0.3.1, 19 October 2026”require_userandverifyrefuse a token whoseaudis a list, even one holding the app’s id. The edge writes one string, andrequire_webhookalready refused a list.- A request with two
X-Forculus-Identityheaders is refused, as the Node SDK refuses it. It read the first.
0.3.0, 16 October 2026
Section titled “0.3.0, 16 October 2026”- An issuer or key set on http is refused unless
FORCULUS_DEVis1, and even then only on this machine (127.0.0.1,localhostor[::1], and for the keyshost.docker.internaltoo).forculus devsets it for the app it runs, from CLI 0.1.11. The platform’s issuer and keys are https and it never setsFORCULUS_DEV, so a deployed app that still points atforculus devrefuses everyone, and asks nobody for keys. Confighas a new public field,dev, whichConfig::from_envreads fromFORCULUS_DEV.Verifier::newrefuses an http issuer or key set unless it’s true. AConfig { .. }literal needsdev: false.ConfigisDefaultnow, so..Default::default()works too. That is why this is 0.3.0.
0.2.0, 6 October 2026
Section titled “0.2.0, 6 October 2026”Userhasagent_name: the name of the workspace’s agent that made the request, orNonefor a person, a bypass token or a scheduled job.user.is_agent()says whether an agent called. Decide what an agent may do bysub, which isagent:<agent id>. The name can change.- An agent’s identity token with no name, an empty one, or an empty agent id is refused, as a person’s with no email is. The platform always sends one.
Userhas a new public field, so aUser { .. }literal needsagent_name: None. That is why this is 0.2.0.
0.1.1, 1 October 2026
Section titled “0.1.1, 1 October 2026”- The README is rewritten in plain words. Nothing in the SDK’s behaviour changed.
0.1.0, 25 September 2026
Section titled “0.1.0, 25 September 2026”First release, so there is nothing to upgrade from.
Verifier::require_userandVerifier::verifycheck the identity token the platform sends with a person’s or a bypass token’s request. Theaxumfeature adds theForculuslayer and theUserextractor.Verifier::require_webhookandVerifier::verify_webhookcheck the token it sends with a webhook delivery whose signature it verified, and name theProvider. Theaxumfeature adds theForculusWebhooklayer and theWebhookextractor. Each kind of token is refused where the other is expected.
The API client for Node.js
Section titled “The API client for Node.js”@forculus/client on npm. Its docs.
0.1.1, 19 October 2026
Section titled “0.1.1, 19 October 2026”- A share or a new member’s reply says which day’s budget stopped the invitation email, when one
did:
budget_spentisworkspace,senderorplatform, besideinvited: "over_budget". - A share in the role the person already had says
unchanged: true. Nothing changed and nothing was logged.
0.1.0, 7 October 2026
Section titled “0.1.0, 7 October 2026”- First release. A client for the Forculus API: workspaces, members, apps, versions, deploys of Workers and static sites, sharing, bypass tokens, the audit log, secrets and config values.
- Signs in with a CLI session:
token,FORCULUS_TOKEN, or the oneforculus loginsaved. - Tries reads again after a failure on our side or a 429, and never sends a change or a deploy twice.
- Sends a deploy’s lock files and
.forculus-allowfor its scan, when given them asscan. - Sends a saved CLI session only to the API it was made by.
- Asks for the app’s key-value storage and queue with a Worker deploy, as forculus.json does:
kvandqueue.
The API client for Python
Section titled “The API client for Python”forculus-client on PyPI. Its docs.
0.1.1, 19 October 2026
Section titled “0.1.1, 19 October 2026”- A share or a new member’s reply says which day’s budget stopped the invitation email, when one
did:
budget_spentisworkspace,senderorplatform, besideinvited: "over_budget". - A share in the role the person already had says
unchanged: True. Nothing changed and nothing was logged.
0.1.0, 7 October 2026
Section titled “0.1.0, 7 October 2026”- First release. A client for the Forculus API: workspaces, members, apps, versions, deploys of Workers and static sites, sharing, bypass tokens, the audit log, secrets and config values.
- Trusts what the operating system trusts, through truststore, its one dependency.
- Signs in with a CLI session:
token,FORCULUS_TOKEN, or the oneforculus loginsaved. - Tries reads again after a failure on our side or a 429, and never sends a change or a deploy twice.
- Sends a deploy’s lock files and
.forculus-allowfor its scan, when given them asscan. - Sends a saved CLI session only to the API it was made by.
- Asks for the app’s key-value storage and queue with a Worker deploy, as forculus.json does:
kvandqueue.