Changelog

What changed, and when.

Everything notable we ship, in plain language — including the things we got wrong and fixed. We publish the fixes as readily as the features, because a changelog that only lists wins is marketing.

changed Candor Deploy

Candor Deploy v1.2.48

  • Persistent volumes for apps. A project can now hold volumes: disk that keeps an app’s files (uploads, a SQLite database) across deploys and restarts. Create one with a name, a size and a mount path, and attach it to one app or worker; detach it (the files stay) or delete it (the files are gone, with no snapshot), both after typing its name. You can raise a volume’s size allowance but never lower it; raising it doesn’t add space to a disk that is already full. Volumes and buckets share your plan’s storage allowance: Free 1 GB (no more), Solo 5 GB, Studio 25 GB, Org 75 GB, PAYG 10 GB. Storage past that isn’t billed yet; it will be billed at $0.02 per GB-month later (up to each plan’s cap). While a volume is attached, a deploy stops the old instance before starting the new one, so expect a few seconds of downtime. A volume lives on one server and isn’t replicated or backed up. The REST API is under /v1/orgs/{slug}/projects/{key}/volumes (and /v1/orgs/{slug}/volumes for every project), and the MCP server has new list_volumes, create_volume, resize_volume, attach_volume, detach_volume and delete_volume tools. Deleting needs app.delete.

  • Buckets: private, S3-compatible object storage in a project (coming soon). Buckets are not available yet. Once they are, create one in a project (“New > Bucket” in the portal, POST /v1/orgs/{slug}/projects/{key}/buckets, or the MCP’s create_bucket). Buckets are private. Attaching one to an app writes AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (marked secret), AWS_REGION, AWS_ENDPOINT_URL_S3, S3_ENDPOINT and the bucket’s name (S3_BUCKET, or a name you choose) into its environment, so any S3 SDK works without pasting keys. Redeploy the app to apply them. Neither the API nor the MCP attach_bucket tool ever returns the keys. The bucket page has a Files browser with short-lived download links. Deleting a bucket removes every file in it and needs the bucket’s name typed again; it takes app.delete (owners and admins), as deleting a database does. Listing and browsing take app.database.read, creating app.create, attaching app.database.manage. Buckets count against the same storage allowance as volumes (egress is free). A new bucket is refused once storage is at the plan’s cap or its bucket count is reached. A suspended project, or one being deleted, can still delete its buckets. MCP tools: list_buckets, create_bucket, attach_bucket, delete_bucket.

  • Viewers no longer see database passwords. Below Developer, a database’s connection details (portal, GET /v1/services/{id}/connection and the MCP get_connection tool) show host, port, database and username, and a scheme://host:port/database address with no user or password, for Postgres, MySQL and Supabase alike. The password and any credentialed URI are withheld, and for a Supabase so are the service-role key and the Studio password; its API URL and anon key are still shown. This applies to viewers and to API tokens created by a viewer, and the connection details carry a note that the rest needs the new app.database.credentials permission (Developer or above). Developers, admins and owners still get the full credentials, and each full read is recorded in your organization’s audit log. An app’s environment screen (and MCP list_env) now masks, for everyone, any value that is a URI with a password in it, such as a DATABASE_URL attached before 30 September that was stored unmasked. Your app still receives the value unchanged. Read the full DSN from the database’s connection details. Masking only changes what is shown: saving the environment, from the editor or with MCP set_env, keeps such a value exactly as it was stored, so preview deployments keep inheriting it. Only the user:password@ form is detected. A password you saved unmasked yourself in some other form, such as a plain DB_PASSWORD variable or ?password= in a URI, is still shown as you saved it, so mark it secret.

  • Storage usage meter on organization billing. The organization’s billing page shows “Storage: X of N GB used” for volumes and buckets together, split by project and by volume or bucket, and how far it is over the allowance now. Overage isn’t billed yet; it will be billed at $0.02 per GB-month later (Free has no overage: it is capped at its allowance). Until then the overage accrued this billing period reads “not available” (null) rather than $0. Database disk is listed on its own line and doesn’t count toward the allowance. GET /v1/orgs/{slug}/storage returns the same figures; members see their own projects, and only organization billing readers see the accrued overage fields. The MCP whoami tool reports it on each organization as organizations[].storage (allowance_gb, used_gb, overage_gb, overage_gb_months, overage_usd).

  • Your plan belongs to your organization. The organization’s billing page and GET /v1/orgs/{slug}/plan show one plan for the organization, one status, its pool (RAM, apps, databases and storage allowance, with what’s in use) and each project’s share of it. Status uses one set of words everywhere: Active, Trial (before billing is set up, running on the included credit), Past due, Suspended, Cancelled, so an invoiced project no longer reads “Not subscribed” next to “Current plan”. Owners, admins and billing members change the plan for every project at once with POST /v1/orgs/{slug}/billing/plan. Moving one project to another plan is refused once an organization has more than one project. Members without billing access see whether projects are running, paused or cancelled, but not past due or trial. The MCP whoami tool reports the plan on each organization (organizations[].plan) and moves the old workspace layer to internal_workspaces, marked internal. The top-level plan/quota_mb/tenant_id fields stay for older clients.

  • Organization spend cap, with emails at 75% and 90%. An organization can now set one monthly cap on usage charges across all of its projects, on the portal’s Billing page. The MCP server’s new get_spend_cap and set_spend_cap tools need an organization token, which the MCP doesn’t accept yet, so use the portal for now. Owners, admins and billing members can change it; anyone who can see billing can read it. Owners and billing members get an email when usage reaches 75% and again at 90% of the cap. When it reaches the cap, we pause the projects that are billing usage (nothing is deleted) and send one email listing them. They start again within about 10 minutes of the cap being raised or removed, or when the next month begins. Plan fees don’t count toward the cap. A project’s own spend cap still works, as an optional budget within the organization’s.

  • Billing is per organization. A new organization bills on one card for the organization; per-project billing can no longer be chosen when you create an organization or switch to it later (POST /v1/orgs and POST /v1/orgs/{slug}/billing-mode answer 400 for "project", unless the organization is already on it, which is a no-op). An organization already on per-project billing keeps working, and can still move to organization billing while it has at most one project and no billing yet. Asking for the billing mode an organization already has now answers 200 and changes nothing. GET /v1/me, and so the MCP whoami tool, reports billing once per organization in a new billing field (scope and a one-line summary, which says “invoiced to the organization by contract” for invoiced organizations) and never per project. billing_mode there is deprecated.

  • Invoices on the organization’s Billing page. GET /v1/orgs/{slug}/billing/invoices (needs org.billing.read: Owner, Admin or Billing) lists the organization’s finalized invoices, newest first, with date, total, amount still owed, status (open, paid, void, uncollectible) and links to the PDF and Stripe’s hosted page. Where each project pays for itself, the invoices of every project the caller holds project.billing.read on are merged and each names its project; if Stripe can’t be read for some of them, the rest still come back with incomplete: true. ?limit= takes 1 to 100 (default 24) and has_more says older ones exist. The MCP’s new read-only list_invoices tool reads the same list but needs an organization token, which the MCP doesn’t accept yet. Cards are still added or replaced only in the dashboard.

  • API tokens live in your organization’s Settings. Tokens belong to an organization, so you now create and revoke them under the organization’s Settings → API tokens in the portal instead of under Account. Account keeps your identity and password and links to each organization’s tokens. The MCP server’s instructions, its “Not authorized” error, the connector sign-in page and the candor login prompt point to the new place and say that an organization owner or admin creates them. They (and both token managers) also say to limit the token to one project, which the MCP connection and the CLI still need. The connector approval page no longer names a place to revoke it. The sign-in page’s “Get a token” button still opens Account, which links to each organization’s tokens. Existing tokens keep working unchanged.

  • Clearer wording: “app” instead of “service”, “GB RAM” for memory. Errors from the API and the MCP server’s tool titles, descriptions and errors now say “app” (or “app or database”, “worker”, “static site” as fits) where they said “service”, for example “App and worker limit reached (3).”, “Unknown kind: ‘wrkr’.” and “Custom domains apply to apps and static sites only.”. Memory sizes in tool descriptions say “GB RAM”, and the delete tools’ titles say they remove databases too. Attaching a database that is not in the project now answers “No such database.” Tool names, arguments and response fields are unchanged (create_service, service_id, scope="service" still work). The monitoring tools point at whoami when an organization slug may be wrong. Turning on single-origin routing is no longer refused because a worker or a database shares the project’s name (they serve nothing at that host).

  • One place for all your custom domains. A project’s Domains tab and your organization’s Domains index list every custom domain with the app it points at, the DNS record to create, whether DNS resolves there yet and whether its certificate has issued. You can add a domain to an app or to a project’s single origin from there, and removing one there asks you to type its name again. The MCP has matching list_domains, add_domain and remove_domain tools (remove_domain needs the hostname repeated as confirmation). Plain http:// to every domain, new ones included, redirects to https://. Managing domains needs app.domains.manage (Developer or above) on every app the domain serves, and every add and removal made in the portal, the API or the MCP is recorded in your organization’s audit log. candorhost.app itself (not just names under it) and internal names (.local, .internal, .svc and similar) can no longer be added, and a domain can only be in use once across all of Candor. With Cloudflare for SaaS on, a domain that has no Cloudflare hostname is now told to point at the edge, which is what serves it.

  • Attaching a database now needs permission to manage it. Attaching one, or a .candorfile deploy that wires in the project’s existing database, now needs app.database.manage (Developer or above) on that database; otherwise attach answers 403 and the deploy fails with the reason. A push or staff redeploy may only wire in the app’s own database, and is refused for any other.

  • Deploys finish even when your whole memory pool is in use. A deploy normally starts the new version next to the old one and switches over once it is ready, so your app never stops serving. That needs room in your pool for a second copy of the app for a moment. With all of the pool allocated (for example the trial pool’s 256 MB app and 512 MB database) there was no room, and the deploy stayed on “deploying” forever while the old version kept running. Now, when the room is not there, the old version stops first and the new one starts in its place. Your app is briefly unavailable while it starts (usually seconds), and never uses more memory than your plan. If the new version doesn’t come up, the deploy is marked failed with the reason; roll back to the last good deploy to restore it. To keep switch-overs seamless, leave free memory in your pool at least the size of your largest app. A deploy whose new version doesn’t fit in the pool even on its own now fails with a message saying so, instead of waiting, and the message says whether memory or your plan’s limit on running instances is what ran out. A deploy replaced by a newer one before it finished is now shown as canceled, rather than taking on the newer deploy’s result.

  • HTTPS certificates are issued and renewed even when your whole memory pool is in use. Let’s Encrypt checks a hostname by briefly running a small helper next to your apps. That helper counted against your memory pool, so with all of the pool allocated it was refused. A new app or custom domain then stayed on an untrusted certificate, and an existing certificate could not renew. The helper no longer uses your pool.

  • An app connected to a repository by its owner/repo name now builds on every deploy, not just the first. A later deploy, Redeploy, rollback or preview of a public repository failed with “does not appear to be a git repository”. Those builds now clone https://github.com/owner/repo.git.

  • Repositories are saved in one form. A GitHub repository given as owner/repo, github.com/…, www.github.com/…, a GitHub URL, or with .git, a trailing / or @branch, is saved as https://github.com/owner/repo.git (@branch becomes the branch). A repository on another host is given as its full clone URL and saved as is. A bare owner/repo always means GitHub. A value with spaces, or one that names no repository (a/b/c, host.example/a/b, a GitHub /tree/ link, file://), is refused with a 422 that says why.

  • Every deploy of a new commit now honours your .candorfile’s kind, memory, env and database, not just the first one. A portal deploy, a git push, Redeploy and a staff redeploy all re-read them at the commit being built:

    • kind: app on a static site turns it into an app. It keeps its domains, and moves to 512 MB unless the file names a memory. The app starts in place of the static site before the static site is removed, so a failure leaves the site serving.
    • Other kind changes (app to static, to or from a worker) aren’t done on deploy. It deploys as the kind it is, and an audit event records that kind wasn’t applied and why. Create a new one of the other kind to switch.
    • An explicit memory resizes the app.
    • env keys the app doesn’t receive yet, from its own env or the project’s shared env, are added. {generate: …} secrets are minted only if missing, so existing values are never rotated, overwritten or shadowed.
    • A database the app isn’t wired to yet is attached from the project’s existing database of that engine, or provisioned if there is none.

    If the project’s memory pool can’t hold the app, the deploy fails with that reason and the app is left as it was. A rollback, and a Redeploy after a rollback, rebuild the older commit without re-shaping the app.

  • Apps can call other apps’ *.candorhost.app addresses again. A request from inside an app to any hostname served by the platform (your own other app, a teammate’s preview, the API) was refused with “connection refused”. It now connects.

  • Supabase connections now include the Postgres details. The connection view, GET /v1/services/{id}/connection and the MCP get_connection tool now return the stack’s own Postgres for a Supabase database:

    • host (the in-project supabase-<name>-db address), port 5432, and database and user postgres (the stack’s admin role, not a superuser);
    • for Developer and above only, the password and a ready-made database_url (postgresql://…). Viewers get the address without credentials, as described above.

    The uri is still the Supabase API URL. The anon key is unchanged, and so is the service-role key for Developer and above. In the portal’s Connect drawer the API URL is shown unmasked and labelled as such, and the Postgres details sit in their own section with the password and DATABASE_URL masked. Attaching a Supabase still sets only SUPABASE_URL and the two keys. If an app needs SQL access, copy database_url into its DATABASE_URL yourself.

changed Candor Deploy

Candor Deploy v1.2.40

  • Delete services and projects from Claude. The Candor MCP server has two new tools, delete_service and delete_project, so you can ask Claude to remove an app or a whole project. Both are permanent and are marked destructive, so Claude asks you before running them; they need an API token with the app.delete permission.
  • API tokens now act with your role in the organization. A token you created in the dashboard used to be limited to a single project, where it could only act as a developer, even if you own the organization. New tokens are organization tokens by default and act with whatever role you hold there. To keep a token to one project, choose it under Limit to project. The token list now shows each token’s scope. Tokens you already have are unchanged, so to get an organization token, create a new one. Scripts that mint tokens through the old /v1/account/tokens route without naming a project now get a token in your name with your full role in the organization (full access if you are an owner), not a project-only one.
changed Candor Deploy

Candor Deploy v1.2.39

  • Manage your GitHub connections. The import page now lists your connected GitHub accounts, so you can add another account or organization, change which repositories Candor sees, or disconnect one from a project. A GitHub organization can now be connected to more than one Candor project at once — connecting it in one no longer disconnects it from another.
changed Candor Deploy

Candor Deploy v1.2.37

  • Importing a repository works again with more than one project. The import page no longer stops at “Connect GitHub” with a “Name a project” error; it lists your repositories for the project you’re in.
  • See when your credit expires. The billing page now says how much of your credit expires soonest, and when, for any credit that has an end date.
changed Candor Deploy

Candor Deploy v1.2.23

  • Change your password from your account page. Account → Password lets you set a new password with your current one, or email yourself a link to set one if you sign in with a code or have forgotten it.
changed Candor Deploy

Candor Deploy v1.2.25

  • Chat with us on candordeploy.com. The chat bubble on the site now reaches the Candor team directly — the same inbox that answers the portal’s Help chat.
changed Candor Deploy

Candor Deploy v1.2.26

  • Two-factor sign-in. Signing in now asks for a code from an authenticator app. The first time, the sign-in page shows a QR code to set one up.
changed Candor Deploy

Candor Deploy v1.2.18

  • Resizing a database’s memory now takes effect. It used to update the number shown and the memory pool, but never the running database. The database now restarts on the new memory straight away — a brief interruption while it comes back up; its data is untouched.
  • Supabase Studio is findable. With the full stack on, a Supabase database’s connection details now include Studio’s address and its sign-in (user supabase and the generated password, masked until revealed). Studio was there and protected all along; nothing told you where it was or how to get in.
changed Candor Deploy

Candor Deploy v1.2.19

  • Signing out of the portal now ends your session on our side too, so a copy of the session cannot be used afterwards.
changed Candor Deploy

Candor Deploy v1.2.20

  • Sign in on Candor’s own pages. Signing in, creating an account and resetting a password now happen on deploy.candorhost.com itself — with your email and password, or a six-digit code by email — instead of a separate sign-in site. Signing out and coming back always asks for your credentials.
changed Candor Deploy

Candor Deploy v1.2.16

  • Database connection strings are hidden for apps deployed from a repo too. The previous release hid them when you attached a database by hand; this covers the databases Candor provisions for you when it deploys a repository, which is how most apps get one. MySQL’s generated passwords are hidden as well. Your app still receives all of them unchanged.
changed Candor Deploy

Candor Deploy v1.2.14

  • Your database URL and generated secrets stop being readable back. A database connection string carries its password, and a {generate: …} value in your .candorfile is minted by us and exists nowhere else — both are now treated as secrets, so the environment screen and the Claude connector show the name and withhold the value, exactly as they already did for anything you marked secret yourself. Your app still receives them unchanged, and a plain literal from your .candorfile stays readable, since it is sitting in the repository you committed.
changed Candor Deploy

Candor Deploy v1.2.12

  • A free project can actually run a database now. Raising the memory allowance was half of it; the storage allowance was still too small for a database’s disk, so one would be created, reported as ready, and never actually start. Both fit now.
changed Candor Deploy

Candor Deploy v1.2.6

  • Claude stops asking permission to look things up. Every tool the Candor connector exposes now says whether it only reads or actually changes something, so asking what you have deployed, whether a build finished, or why one failed just answers — while setting an environment variable or redeploying still asks first, because those change a running app.
changed Candor Deploy

Candor Deploy v1.2.9

  • Free now runs an app and a database. It was 512 MB, and a database always takes 512, so a free project could hold one or the other and a deploy that wanted storage was refused outright. The pool is 768 MB, and an app onboarded alongside a database is sized to fit beside it instead of failing. Existing free projects get the extra room automatically; nothing to do.
changed Candor Deploy

Candor Deploy v1.2.4

  • Your billing page now shows your credit and your card. The launch credit — and any credit our team has added — was applied to your account but shown nowhere; the organization’s billing page now says how much is left, and which card (brand, last four digits, expiry) your invoices charge. An organization without a card on file yet is told what adding one unlocks.
changed Candor Deploy

Candor Deploy v1.2.2

  • $100 in free credit, on us, when you start. Every new account gets a one off $100 credit the first time it puts a card on file — a launch special, not a trial: nothing expires, nothing switches off, and the credit simply draws down against your invoices until it is gone. One per account rather than per organization, so it is the person we are welcoming, not the number of workspaces they create.
changed Candor Deploy

Candor Deploy v1.1.0

  • Connecting Claude or ChatGPT to Candor works again. “Add Candor” from claude.ai, ChatGPT or Claude Code sends you to the same sign-in the portal uses, then asks which project the assistant may deploy to, and you’re connected — no token to copy. The old consent screen still expected the retired client area, so nobody could get past it.
  • Organizations. Your workspace is now a project inside an organization, and you can invite people to it by email with a role — owner, admin, developer, viewer or billing. Roles inherit from the organization down to each project and each app, and can be overridden for one project or one app when somebody should have more (or less) there than everywhere else. An invitation can be scoped that way too: invite a contractor to one app and one app is all they can reach. Everything you had is already inside an organization we made for you, and you own it.
  • Billing belongs to your organization, not to one person’s account. The card on file, the plan, the invoices and the payment emails all attach to the organization now, so a colleague leaving does not take the billing with them, and anyone you give the billing role can handle the card and the plan without also being an owner. Payment emails — card declined, workspace paused, payment received — go to every owner and every billing member, and to nobody else. One card and one subscription cover every project in the organization: usage is billed once, and each plan you have projects on appears as a single line on the invoice with the number of projects on it. Usage is priced at your organization’s highest plan — an organization with a Studio project is charged for usage at Studio’s rate and gets Studio’s included usage across every project in it, so a project still on Free rides on the better deal rather than dragging the rest down to the worse one. Move a project up and the rate follows it the same day, with no charge for the change itself; move the last such project away and it comes back down.
  • You can now put the card where the bill is. Adding or replacing a payment method happens on your organization’s billing page, not on a personal account page — so if you run two organizations, each one holds its own card and its own invoices instead of both quietly charging the first one. If you would rather have a card per project, choose per-project billing when you create the organization: each project then has its own billing page and its own card, and adding one starts that project billing straight away with its full spending headroom while the rest of the organization is untouched. The choice cannot be switched once billing has started. Plan changes and spend budgets live on the same pages, one project at a time, and your organization’s billing page shows what each project used this month, so a single invoice line still tells you where the money went. Nothing changes for an existing account: the card you already have is the card your organization already uses, and the old account billing page keeps working for one more release.
  • If you call the API directly: a workspace is created at POST /v1/orgs/{slug}/projects now. The old POST /v1/workspaces answers 410 Gone and names its replacement. Your existing CLI token keeps working — it was migrated to a token scoped to the project it already reached.
changed Candor Deploy

Deploys that fail tell you why

A deploy could build fine and then never come up — and sit there saying “deploying” until you gave up on it. Now we diagnose it and say so: the container exited immediately, or it crashed on startup, or it never became ready, with the exit code and the last lines of its output.

We also stopped requiring you to know your start command. We detect it from your project, tell you what we detected, and if we genuinely cannot work it out, we say that plainly rather than guessing and failing strangely.

added Candor Deploy

Preview deployments

Push to any branch other than the one your service tracks — or open a pull request — and Candor Deploy builds that commit at its own URL. Production is untouched. Pushing again updates the same URL, so a link you paste into a pull request keeps working.

Previews get your production environment variables except the ones you marked secret, which are never copied into a preview. Give previews their own values under Environment → Preview.

They clean themselves up: a preview disappears when its pull request closes, when its branch is deleted, or after seven days with no pushes. Your plan includes a set number at a time, shown on the Previews tab, and each one runs in 256 MB.

added Candor Deploy

Rate limits and IP rules

A Protection tab on every app and static site. Set a per-visitor request rate, or allow/deny specific IPs and ranges. Changes apply at our edge in seconds — no redeploy, no restart.

Being straight about what this is: edge rate limiting and IP filtering. It is not a WAF, and we will not call it one. Blocked requests show up as 4xx on your Analytics tab so you can see it working.

added Candor Deploy

Scheduled jobs

Add a cron schedule and a path, and we call that path in your app on time, every time. Run history and a run-now button are in the portal, so you can see what fired and when instead of guessing.

Requests carry a header secret so only we can trigger them, and they go to your app’s internal address — never through the public internet. How many you get is published on the tab, not discovered when one silently stops.

fixed Candor Deploy

Your apps see real visitor IPs

Apps behind our edge were receiving an internal cluster address as the client IP rather than the visitor’s real one. Anything you built on the client address — rate limiting of your own, geo lookups, audit logs, abuse handling — was working from the wrong value.

Fixed at the edge. Your app now sees the visitor’s actual IP, and so do the new rate limits and IP rules.

changed Candor Deploy

Readable logs, and one domain for a whole project

Logs were a wall of raw text. They now parse into rows with timestamps and levels, filter by level, search as you type, and live-tail. Structured JSON logs are unwrapped so you read the message, not the envelope.

Separately, a project can serve every one of its services under one domain, with each service answering on its own path. Your frontend and your API on the same origin, which is what most apps want and what removes a class of CORS problems entirely.