Changelog
Notable changes to this documentation site, latest first. The FilesHub product's own release notes live with the app at fileshub.zaions.com.
2026-09-04 — a 22nd developer-account provider, and formats that both write planes enforce
- Developer accounts now lists 22 providers, adding
native_update— the account-wide token that administers every over-the-air app on a native-update account. Backend2026.09.04.1. - 🔴 New section: a provider name may also be a project-vault service. Six names appear in both places, and the page now says plainly why that is not duplication: the vault row is that project's configuration (a DSN, an extension id, an OTA app id and its device key), the developer account is the credential that administers every one of them. A DSN says where errors go; an auth token can delete the project.
- 🔴 New section: a field may declare a format. A value that does not match is refused on write with 422
INVALID_DEVELOPER_ACCOUNT_CREDENTIAL, anddetailsnames the provider and field — never the value, because an error that echoes what was sent puts a credential into the response body, your log and your CI transcript. Documented with the real error body. - The rule this closes was real: the format check ran in the admin form and not on the API, so a value the form refused could still be stored over the write plane. Two planes disagreeing is worse than neither checking.
2026-09-01 — developer accounts, debug keystores, and the two releases this site had missed
- 🔴 New page: Developer accounts. The tokens that publish under your name — npm, Hugging Face, Docker Hub, PyPI, crates.io, the extension stores, Vercel, Netlify, Sentry — on one account-level record, because a publish token authorises an account, not a project. One npm token publishes every package you own; copying it into each project's vault is not storing one credential, it is storing fifty copies and rotating means finding all fifty. Backend
2026.09.01.1. - 🔴 The reveal hands back a
.npmrcline, not a string.config_filescarries the literal dotfile content, so CI writes what it was given. A job that re-derives//registry.npmjs.org/:_authToken=gets the prefix subtly wrong once and then cannot publish — while the error blames authentication. There is noviteornextblock and there never will be: every field on a developer account authenticates a publish, so a browser bundle is exactly where none of it belongs. - 21 providers ship declared, and the page tells you not to trust that number —
GET /developer-accounts/providersreturns the live list, because adding a provider is a config entry that needs no release note. - 🔴 New page: Debug keystores — shipped in backend
2026.08.31.2and undocumented here until now. A debug keystore is generated per machine, not per app, so it is stored once and every project's vault payload carries it as a derived block. The page leads with why you care: Google Sign-In, App Links and Firebase each fail in a way that never mentions signing, and all three are a fingerprint nobody wrote down. - 🔴 No new scope for debug keystores, and the page says why. A new scope defaults to
falseon every token that already exists, so minting one 403s every caller until each is edited. That cost is worth paying for a credential and not for a fingerprint, which ships publicly inassetlinks.jsonand authorises nothing alone. Developer accounts made the opposite call, for the opposite reason. - 🔴 Authentication listed five scopes; there are eight.
can_write_vaultshipped in2026.08.20.1and appeared nowhere on that page — not in the table, not in theGET /tokensample.can_read_ai_accountsandcan_read_developer_accountswere missing too. All three are now documented, and the sample payload shows the full set. - 🔴 And a sentence on that page was wrong, not merely incomplete: "Only
can_reveal_vaultimplies another".can_write_vaultalso impliescan_read_vault— a caller populating a vault must be able to see which fields are already set. Corrected, with the three non-implications spelled out, because a scope that looks like it should imply another is exactly where a caller guesses. - OpenAPI: 44 → 54 paths. All ten developer-account and debug-keystore operations, linting at 0 errors / 2 accepted warnings — the documented baseline.
llms.txtandllms-full.txtcarry both subsystems, so an agent reading the machine-readable mirror gets the scope rules and theconfig_filescontract without loading a page.
2026-08-24 — Cloudflare Turnstile, and the Supabase release the site had missed
- 🔴 New page: Verify a Turnstile token.
POST /api/v1/turnstile/verifychecks a Cloudflare Turnstile token using the secret key stored in your project's vault, so the secret never has to reach your frontend. A static or Firebase-hosted app with no backend of its own can now use Turnstile without standing up a worker for one form. - 🔴 The page leads with the thing integrations get wrong: a failed challenge is
200, not4xx. A visitor failing a captcha is a normal outcome. Were it an error status, an integration would have to tell "FilesHub is broken" from "the visitor failed" by parsing a message — and one that skipped that step would treat an outage as a bot and reject every real person. Branch onsuccessfor the verdict, on the status code for whether the verdict means anything. The two409s stay distinct because store a secret and re-enter it are different fixes. - New vault service
turnstile, taking the registry to 22 services / 101 fields. Deliberately separate fromcloudflare: a project uses Turnstile without owning any Cloudflare account config, and merging them would makeconfigured_servicesreport an account that does not exist. - Action checking is documented — without it, a token minted on your signup form is a perfectly valid token on your login form. A mismatch returns
fileshub-action-mismatch; the prefix marks it as ours so a future Cloudflare code can never silently collide. - 🔴 Supabase projects was a release behind and is now current. It documented
keepalivewithoutenabled, and neitherendpoints.jwksnorconfig.jwt— all three shipped in backend2026.08.23.1. Added: the two-switch explanation (is_activeretires a registration,keepalive.enableddecides whether FilesHub may write to that database — a client project isactive: true, keepalive.enabled: false), the?keepalive=filter, and the JWT signing-key fields with the reasonendpoints.jwksis derived rather than stored. - 🔴 A
keepalive.last_statusthat never moves is not a broken project whenenabledisfalse— it is one deliberately left alone, and a skip writes no run row at all. That sentence is in the docs now because the payload alone reads like a failure.
2026-08-22 — one AI account for every project
- 🔴 An AI account can now serve every project, including projects created later. The owner keeps one or two provider accounts for a whole fleet, so the common case is not "assign this key to a project" but this key is the fleet's key. Set
all_projectson the account and there are no pivot rows to maintain — and no 56th project quietly created without it. - Two new routes, the account-centric direction:
GET|PUT /ai-accounts/{account}/projects. The existing pair assigns many accounts to one project; this assigns one account to many projects. Both needcan_write_vault, and neither ever returns a key. - 🔴
{"all_existing": true}andall_projectsare different features, and the docs now say so in a table. The first is a snapshot — every project that exists right now, future ones excluded. The second is a standing rule. Sending both in one body is a 422 rather than resolved by precedence: "all_existing wins" and "the explicit list wins" are equally defensible, so a caller should never have to guess which was implemented. - Each resolved entry reports
via: explicit | all_projects, which is what tells you where to undo it — anall_projectsentry is detachable from the account, never from the project. - 🔴 A note on a name collision: an access token also has an
all_projectsfield, meaning which projects you may reach. An AI account flagged for every project never widens a token's scope. - Corrected a stale box on Endpoints: it claimed no project had any configured service, a figure measured through a read path fixed in
2026.08.19.1. The honest count, probed 2026-08-19, is 7 of 55 — with a warning thatconfigured_servicesnow includes the derivedfileshubservice and so cannot be read as "someone entered data".
2026-08-21 — AI provider accounts
- An OpenAI or Anthropic key now lives on its own account record, not in each project's vault. That key authorises an account: it spends its balance and reaches every model on it, and two or three accounts serve a whole fleet — so copying it per project meant one secret in twenty rows, rotated in twenty places, agreeing in none. Same argument that put a Supabase personal access token on its account. Documented in AI provider accounts.
- 🔴 A new scope,
can_read_ai_accounts, andcan_reveal_vaultdoes NOT imply it. The most powerful grant over project credentials still answers403from every/ai-accountsendpoint, deliberately: it is given to a caller that should read one project's configuration, not to hand out billing. Assigning an account to a project is different again and needs onlycan_write_vault, because an assignment is a pointer and never returns a key.GET /tokennow reports six booleans. - 🔴 Many accounts per project, unlike the single Supabase link — an app routinely uses OpenAI for embeddings and Anthropic for chat, and a single foreign key would make that inexpressible.
- A reveal returns
env.nodecarrying the variable the provider's SDK actually reads. 🔴 There is noviteornextblock and there never will be — an AI key in a browser bundle is someone else spending the balance. - 🔴 Clearing a key is
{"clear_api_key": true}, not a blank value. A blankapi_keyis dropped so editing an unrelated field cannot wipe the credential, and it could not be "absent keeps, null clears" like the vault endpoints: Laravel'sConvertEmptyStringsToNullmiddleware rewrites""to null before a controller sees it, so the two are indistinguishable and that rule would have destroyed the key on any empty string. openaistays a declared vault service, marked superseded. A data migration lifted stored values onto account records additively — the originals were left in place, because deleting a credential is an owner decision. Removing the service would have orphaned every one of them.- OpenAPI spec extended to 1.5.0.
2026-08-20 — The project vault became writable
- 🔴 The vault now accepts writes, reversing a decision that was reaffirmed as recently as 2026-08-19. If you find a page, a note or a tool description still saying "no credential write endpoint exists or is planned", it is stale — that sentence was true until backend
2026.08.20.1. What changed is scale, not principle: around sixty projects times ninety-odd declared fields is not a workload a form can carry, and most of those values already sit in a local checkout, a git remote, or a console a CLI can read. The admin keeps its per-field help text and stays the better place to type a single value by hand. - Seven routes, documented in Writing to the vault: a partial per-service upsert, an atomic bulk write, a service clear (which requires an explicit
confirm), an orphan move, config-file store and delete, and a link-list replace. Each answers with the same payloadGET /projects/{project}/vaultreturns — so you confirm what landed without a second call and without needingcan_reveal_vault— plus achangesblock naming created, updated and deleted field names, never a value. - A new opt-in scope,
can_write_vault, off by default like the rest. It impliescan_read_vault(a writer must be able to see which fields are already set) and deliberately does not implycan_reveal_vault— a seeding agent should be able to fill a project in without being handed back every secret you already hold. Authentication and the endpoint reference now list five capability booleans. - What the write plane refuses, and why. A free-text service name is rejected on create — that is the entry-side half of the ISSUE-09 story, where a credential stored under an undeclared service name was invisible in three clean
200s for two weeks. An existing orphan stays deletable and movable, because the write side must be able to clean up whatever the read side can see;POST /projects/{project}/vault-moveexists for exactly that and validates the whole move before writing any of it. And for a registry-declared field the registry decides whether a value is secret — a caller-supplied flag counts only on the freeformgeneralservice, where it defaults to secret. - 🔴
fileshubis now a derived service. Its four fields — project slug, API base,fh_live_key and that key's origins — are computed from records FilesHub already holds, and writes to it are refused. Same reasoning that makessupabasea link rather than a copy: a stored second version of a credential the database already owns is two values that can drift with nothing to say which is right.GET /vault/servicesnow publisheswritableandderivedper service so you learn this from schema discovery instead of from a422. - Two new services in the registry:
firefox_addonsandedge_addons— Mozilla AMO (extension id, listing slug, JWT issuer and secret) and Microsoft Partner Center (product id, client id and secret, tenant token URL). The registry is now 20 services, 95 fields. - OpenAPI spec extended to 1.4.0: 2 new paths, 7 new methods on existing paths, and 9 new schemas.
VaultServicePayloadandVaultServiceDefinitiongainedderivedandwritable. Verified 0 errors, and 0 new warnings against the previous spec.
2026-08-01 — Supabase account tokens
- New page: Supabase account tokens. A Supabase personal access token (
sbp_…) belongs to an account, not a project — it drives the Supabase Management API andsupabase login --token, so one token reaches every project that account owns, including projects that do not exist yet. It now lives on its own account record, entered once for all the projects underneath it, and is read back over three new endpoints:GET /supabase-accounts,GET /supabase-accounts/{idOrEmail}andPOST /supabase-accounts/{idOrEmail}/reveal. The reveal returnsenv.cli=SUPABASE_ACCESS_TOKEN=…, sosupabase link/db push/functions deployneed no interactive login. - A new opt-in token scope,
can_read_supabase_tokens, off by default — and the part worth reading twice:can_manage_supabasedoes not grant it. The two have different blast radii, so they are different switches; a token holding onlycan_manage_supabasegets403from every account endpoint. Documented in Authentication (now four separate axes, with which of them imply each other stated explicitly), overview and the endpoint reference. - The Supabase project vault payload gained an
accountpointer — which account owns the project, whether it has a token stored, and the endpoint plus scope that would return it. It is a pointer and never a credential: no project endpoint returns a personal access token, whatever scopes the caller holds. - OpenAPI spec extended to 1.2.0: 3 new paths and 3 new schemas. Token introspection also gained
can_read_vaultandcan_reveal_vault, which the spec had been missing since they shipped — a client generated from 1.1.0 was unaware of both. llms.txtandllms-full.txtgained the account endpoints and the project vault, which had been omitted when that page shipped on 2026-07-31.
2026-07-31 — Project vault
- New page: Project vault. FilesHub now stores every third-party credential, config file and identifier a project needs — Firebase, Google Cloud, Sentry, OneSignal, Clarity, Amplitude, Cloudflare, Capacitor signing, GitHub, the three store consoles, OpenAI, SMTP, plus a freeform bucket — and reads them back over the Management API, including ready-to-paste
.envblocks for Vite, Next, Node and Laravel. Supabase is linked to the existing account-wide vault rather than duplicated. - Two new opt-in token scopes, both off by default and documented in Authentication:
can_read_vaultsees metadata and which credentials exist — presence flags, never a value — whilecan_reveal_vaultsees the values, the file bytes and the env blocks. A token missing one gets403 TOKEN_PERMISSION_DENIEDwithdetails.required_scope, not the anti-enumeration404used for resources; that distinction is now stated in the endpoint reference error table. GET /vault/servicespublishes the field registry itself — every field's label, where in that console to find it, whether it is secret, whether it may ship in a browser bundle, and its.envname. Schema discovery, so an agent never has to guess what a project can hold.- The generated
VITE_/NEXT_PUBLIC_blocks contain only fields markedclient_safe, and a field markedsecretis alwaysclient_safe: false. A server secret is structurally unable to appear in a client env block rather than merely omitted by convention. - OpenAPI spec extended to 1.1.0: 7 new paths and 8 new schemas, all using the 3.1 nullable form. The agent workflow gained a bootstrap-a-machine-from-the-vault recipe.
2026-07-30 — Global origins are readable over the Management API
- Documented two read-only endpoints —
GET /global-originsandPOST /global-origins/check— in the endpoint reference. Global origins apply to every restricted key and never appear in a key's own list, so previously there was no way to see them over the API; the docs said outright that none existed. Writes stay in the dashboard. The agent workflow now checks coverage before adding origins and adds only what comes backcovered: false, and API key restrictions records that the read API exists. - The check endpoint runs the same matcher request-time enforcement uses, because coverage is not string equality:
https://*.example.comcovers its subdomains and the bare apex, an apex rule covers no subdomain, andhttp://localhost:*covers every port. Extended the OpenAPI spec with both paths and theGlobalOriginPublic/GlobalOriginCheckResultschemas, and added the guidance tollms.txt/llms-full.txt. - Fixed 49 wrongly-typed nullable fields in the OpenAPI spec. The spec declares OpenAPI 3.1, but 49 properties used the 3.0
nullable: truekeyword. 3.1 removed that keyword, so it was being ignored — meaning the published spec asserted those 49 fields were non-nullable while the API genuinely returnsnull, and any client generated from it got the wrong types. They now use the 3.1 form,"type": ["string", "null"]. If you generated a client from this spec before today, regenerate it. No API behaviour changed; only the description of it was wrong.
2026-07-22 — Supabase project vault (Management API)
- Documented the Supabase project vault — read and reveal a registered Supabase project's full credential set (API keys, JWT secret, Postgres connection details, S3 keys) plus ready-to-paste React/Node/Laravel
.envblocks, over the Management API. Gated by the newcan_manage_supabaseaccess-token scope (off by default), which is a separate axis from the project scope. - Extended the OpenAPI spec with the
/api/public/v1/supabase-projectsendpoints (list, show, reveal) and addedcan_manage_supabaseto the token introspection; noted the scope in overview and authentication, and added a Supabase section tollms.txt/llms-full.txt.
2026-07-18 — Management API
- Documented the new Management API — a server-side control plane that manages projects, API keys, and origin restrictions with a user-level
fh_pat_access token: overview, authentication, the endpoint reference, and an AI agent workflow for auto-configuring a project's API key. - Extended the OpenAPI spec with the
/api/public/v1/*endpoints under a newBearerAuthscheme, and added a Management API section tollms.txt/llms-full.txt.
2026-07-14 — Full API docs + AI surfaces, GitHub Pages
- Moved to
fileshub-docs.zaions.comon GitHub Pages as the single host (Firebase Hosting removed). - Documented the email API: send (raw + template, multi-domain, queued), templates, and recurring schedules.
- Documented jobs (poll queued operations) and version & health.
- Added API key restrictions — ship a key in a React/mobile frontend with web-origin, Android package + signing-cert, and iOS bundle-id allowlisting.
- Added AI-agent surfaces: an OpenAPI 3.1 spec at
/openapi.jsonand a raw-Markdown mirror of every page under/raw/(index at/raw/manifest.json), with a "View raw Markdown" link on each page. - Added a summarized developer utilities index and expanded the API overview with scopes and the full endpoint map.
2026-06-23 — Initial public docs
- Launched
fileshub-docs.zaions.comas the public FilesHub documentation site (Docusaurus, hosted on Firebase Hosting + GitHub Pages). - Documented the storage object API: upload, download, list, and delete, with exact request/response shapes and the
{ "message": ... }error envelope. - Added Getting Started: Quick Start, Authentication (
X-API-Key, permissions, restrictions), and File visibility (publicvsprivate). - Added Integration Guides: Integrate from any app (JS/TS, React, PHP/Laravel) and Browser & mobile uploads.
- Added a platform services map of the 50+ utility endpoints, an FAQ, and a Privacy note.
- Added SEO/AI-discovery files:
robots.txt(AI-bot allowlist),sitemap.xml,llms.txt,llms-full.txt,humans.txt, and/.well-known/security.txt, plus JSON-LD (WebSite, Organization, SoftwareApplication).