Skip to content

AppTown v1.0.0-rc.6

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Oct 09:59
· 377 commits to main since this release
561b7df

Flokkit AppTown v1.0.0-rc.6

The sixth candidate for 1.0, and the first under the product's new name,
Flokkit AppTown (AppTown for short; it was Flokkit, and the code called it
"forge"). It replaces RC5 as the candidate running in production with the
maintainers, and the two-week run before 1.0 starts again on it.

RC6 renames everything people, agents and apps see from "forge" to
"AppTown", and fixes the app contract that 1.x keeps stable (version 2). It
makes each stock need give an app the same values on every deployment
target. It backs up object storage and Portainer deployments, and gives
every deployment its own database and storage credentials. It adds a
one-command desktop trial over HTTPS. It closes what 1.x could not change
later, removes features nobody used, and fixes defects found in the RC5 run
and in a first desktop trial.

Since 22 October 2026 the code changes on GitHub, at
github.com/flokkit/apptown, one pull request per change.

In short

  • The product is AppTown, and so are its names. The commands are
    apptown, apptownd and apptown-worker. Settings are APPTOWN_*,
    headers X-AppTown-*, and tokens start apptown_. The repository folder
    is .apptown/. AppTown reads none of the old FORGE_* names.
  • Apps get only the new names: APPTOWN_ORIGINS, APPTOWN_RELEASE,
    APPTOWN_BROWSER_ENV*, window.__APPTOWN_ENV__ and /_apptown/, and no
    FORGE_*.
  • The app contract is version 2: memory in Mi/Gi, cpu in cores or
    millicores, and ${needs.<need>.<VARIABLE>} to pass a need's value under
    another name. It refuses values, names and fields that 1.x could not give
    a meaning later, and says what to write instead.
  • Every stock need gives an app the same connection values on every
    target
    , as contract-reference.md sets out. Each deployment has its own
    database and storage passwords.
  • Backups now cover object storage and Portainer deployments, and were
    proven on a real Docker host, App Platform and Portainer. Restore
    rehearsals run on the app's own host when no host is kept for them.
  • What AppTown makes for a deployment is named by the deployment's id,
    under apptown-, with apptown.* Docker labels.
  • Sign-in is the default. apptownd starts with -auth internal unless
    told -auth open.
  • Settings an administrator changes are set in AppTown, not in flags.
    Five apptownd flags are gone, and the flags that remain are promised
    for 1.x.
  • Features nobody used are removed: the backlog, project export, the
    check-only worker, webhook triggers and others (see "Removed").
  • Try it with one command on your own computer, over HTTPS. It installs RC6.
  • Behind a company's TLS-inspecting proxy, AppTown's own connections can
    trust that company's certificate authority. The containers AppTown starts
    do not trust it yet. That comes in RC7.
  • No automatic upgrade from a release candidate. RC6 is for new
    installs. See "Upgrading from RC5". It has thirteen database migrations
    (153 to 165).

What changed

The new name

  • The product is called Flokkit AppTown, AppTown for short. The web
    interface, the server's and the CLI's messages, the guides, the README,
    NOTICE and the GitHub repository (flokkit/apptown) use it. The Go
    module is github.com/flokkit/apptown. The container images are
    ghcr.io/flokkit/apptown, apptown-worker and apptown-agent.
  • Commands, install paths and settings. The daemon is apptownd, the
    CLI apptown, the worker apptown-worker, and the restore gate
    apptown-gate. A server install uses /opt/apptown,
    /etc/apptown/apptownd.env, /var/lib/apptown and apptownd.service (a
    worker: /opt/apptown-worker, /etc/apptown-worker,
    apptown-worker.service). Every daemon flag is set as APPTOWN_<FLAG>.
    FORGE_ variables are not read. The operator secrets are
    APPTOWN_WEBHOOK_TOKEN and APPTOWN_APPS_DOMAIN. The agent image's
    default tag is apptown-agent.
  • The built-in database of a new install is the container apptown-db
    on the volume apptown-data-apptown-db, and the database inside is
    apptown. db.env records these names, so an install made before 1.0
    keeps its own. The trial keeps its data in /var/lib/apptown/data. The
    SSH host alias for an attached Docker host is apptown-target-<name>.
  • The CLI and agents. The CLI reads APPTOWN_URL, APPTOWN_TOKEN,
    APPTOWN_USER and the rest, and its credentials from
    ~/.config/apptown/credentials. Agent steps and chats get APPTOWN_*
    variables and see AppTown's inputs at /apptown/in and /apptown/state.
    The MCP server is named apptown and its scopes are apptown:*.
  • What leaves AppTown. Request and response headers are X-AppTown-*
    (and AppTown-Restore-Token, AppTown-Gate, AppTown-Head); new commits
    carry AppTown-* trailers (commits already written keep Forge-*); new
    tokens start apptown_ (apptown_invite_, apptown_mcp_, apptown_git_
    and so on); the session cookie is apptown_session; metrics are
    apptown_*; the OpenVEX document names apptown:project/<slug>. AppTown
    names itself apptown to outside systems it sets up from now on (a
    registry login, a Git remote user, a GlitchTip team). Outside objects that
    already exist keep their names.
  • AppTown's own acts read as AppTown. The audit log, a target's History
    and the Usage page show AppTown's own actions as "AppTown"; the stored
    records are not rewritten.
  • The guides were renamed and checked. running-a-forge and
    what-forge-is-for are running-apptown and what-apptown-is-for. Every
    shipped guide was checked against the code and rewritten where it was wrong
    or out of date (for example, apptown dev writes .apptown/dev.env and
    .apptown/Procfile, and a preview stops eight hours after it was last
    looked at). The roadmap guide is folded into "What 1.0 promises", under
    "Not in 1.0".
  • The guidance, workflows and packs agents read say AppTown, and
    messages no longer cite design-document paths.

Names AppTown gives what it makes

  • Named by the deployment's id. Two deployments could share a
    container, a network and a database volume on a Docker host: project
    shop with deployment eu-main and project shop-eu with deployment
    main made the same name. Everything AppTown makes for a deployment is
    now named from its key, the last 12 hex digits of its id:
    apptown-dep-<key> for the app, apptown-svc-dep-<key>-<name> for its
    services, apptown-need-dep-<key>-<need> for a need's data, and so on.
    Portainer stacks, App Platform apps, database clusters, Spaces keys and
    buckets are named the same way.
  • Docker labels are apptown.*. A deployment's containers, network and
    volumes also carry apptown.project and apptown.deployment.name, so a
    person can tell from docker ps whose they are. AppTown never reads
    those two.
  • Moving an older deployment. A deployment made before this keeps
    working under its old names. apptown move-names <project> <deployment>
    moves it. AppTown releases what it runs again and stops it. It copies
    each data volume to the new name and checks every file, owner, mode and
    link of the copy. Then it switches the record and starts the app under
    the new names. The old volumes stay on the host until someone deletes
    them. A copy that does not check out puts the old app back. Portainer
    deployments are not moved: take one down and release it again.
  • Leftovers from older releases. At start, AppTown and each worker
    remove the throwaway containers an older release left with forge.*
    labels (previews, step and chat containers, checks, tasks). A chat or
    preview open at that moment is made again when next used. A deployment's
    own containers and volumes, and the built-in database, are not touched.
  • Throwaway containers take their anonymous volumes with them. Checks,
    tests, tasks, agent steps, a step's services and copy jobs were removed
    without their anonymous volumes. A busy host gained hundreds to thousands
    of unused volumes a day. These are now removed with -v. Named volumes,
    which hold data, are never touched. Standing deployments are still removed
    without -v, and AppTown does not run docker volume prune. Volumes
    already left on a host stay until someone removes them.
  • These names are not promised. "What 1.0 promises" says how AppTown
    names and labels what it makes may change in 1.x. Names already given
    stay.

The app contract

  • Fields that do nothing are refused, by name. A version-2
    .apptown/app.yaml is refused if it uses seed; healthcheck on the
    public part; the health period, timeout, thresholds or checksDeps; a
    tcp or exec readiness probe; dataDir on a part that is not public; or
    sets PORT, APPTOWN_ORIGINS, DATA_DIR or APPTOWN_RELEASE in an
    environment. Each refusal says what to do instead. These were accepted
    and ignored, or behaved differently by target; refusing them now lets a
    later release give them a meaning without changing what existing apps do.

  • What a 1.x release may still change is listed in the compatibility
    guide: the limits it names (health timings and probe kinds, Portainer init
    ordering, where integration values arrive, seed, origins and dataDir
    delivery) may be lifted; nothing else in the contract changes meaning.

  • APPTOWN_ORIGINS and command are written down: the origins format,
    and that a string command runs with sh -c.

  • One exact app contract. contract-reference.md says what an app must
    do and what AppTown gives it: the files read, what a build sees, the
    process, the environment by target and part, startup order, what arrives
    through the front door, health and static sites. Where targets still differ
    (health checks, startup order on Portainer), it says how. "What 1.0
    promises" names it as what 1.x keeps stable.

  • The contract's names (flag day). The repository folder is .apptown/
    (the contract is .apptown/app.yaml). Apps get APPTOWN_ORIGINS,
    APPTOWN_RELEASE and APPTOWN_BROWSER_ENV*, the browser global
    window.__APPTOWN_ENV__, and the static files /_apptown/env.js and
    /_apptown/release. AppTown gives apps none of the FORGE_* names,
    __FORGE_ENV__ or /_forge/ any more. Workflows and packs say
    apiVersion: apptown.dev/v1; a stored one keeps the version it was
    written with. New changes and runs use the branches apptown/change-N and
    apptown/run-N; earlier ones keep theirs. The stock platform pack is
    apptown, and the check trusts-forge-front-door is trusts-front-door.
    Revisions recorded before the upgrade (releases, backups) are read at
    .forge/app.yaml, as they were written.

  • Values reach the app exactly as written. An environment value must be
    text, a whole number or true/false. A value YAML would read as something
    else is refused and should be quoted: 3.10 used to arrive as 3.1, and
    an empty value as <nil>. Values accepted before reach the app as they
    did.

  • Names are checked. Environment names are letters, digits and _, not
    starting with a digit. Names starting APPTOWN_ are kept for AppTown. A
    secret can no longer be named PORT, DATA_DIR or another variable
    AppTown sets: such a value used to win on a Docker host and lose on other
    targets. Part and init step names are 2 to 32 lower-case letters, digits
    and hyphens, as App Platform requires. Parts, init steps and needs share
    one set of names. app, and names starting init- or apptown-, are
    refused. Every init step needs a name, because a secret is scoped to an
    init step by its name.

  • Fields that were ignored are refused. A static site refuses
    environment, command, dataDir and healthcheck. ports lists the
    one port a part listens on: a second entry or a host port such as
    3000:8080 is refused. secrets.spec must name a file inside the
    project.

  • A project's address is fixed. A project's slug (as in shop#12) can
    no longer be changed once the project exists. Renaming it left the
    repository folder, containers, volumes and backups under the old name, and
    a new project given the old slug took over the old repository. The display
    name, icon, line and group can still change.

  • No VOLUME in an app's Dockerfile. A VOLUME line makes Docker
    create a new unnamed volume for every container started from the image:
    checks, previews and releases. AppTown does not back these up or move
    them. The build/container-image guidance, which every stock pack that
    builds an image includes, now says to leave VOLUME out. Its new check,
    no-volume, fails any VOLUME in the Dockerfile's final stage, even at
    the data folder. It says to remove the line, and to declare the folder as
    dataDir if the app keeps data there. A Dockerfile whose heredocs the
    check cannot follow is "not judged". A VOLUME from the base image, and
    Dockerfiles in other build folders, are not judged. Projects get the
    check when they adopt the new pack version.

  • The contract is version 2. Memory is written in Kubernetes binary
    units only (512Mi, 2Gi); a bare 512M is refused with the spelling to
    use, because Kubernetes and Score read M as decimal. Cpu may be
    millicores (500m). A version-1 contract (or one with no version) keeps
    reading M and G as MiB and GiB wherever it is read. Deployment memory
    overrides take Mi/Gi, and pages show memory in Mi/Gi.

  • Environment values may read a need's values, as
    ${needs.<need>.<VARIABLE>} (version 2), for an image that expects its own
    names: WORDPRESS_DB_HOST: "${needs.mariadb.DB_HOST}". The need's own
    variables are still given. It is the same on every target; on App Platform
    a wired value is a secret. $${needs. writes the literal text. A variable
    the need does not give, or an llm value, is refused before a deploy.
    apptown dev gets the wired values too. A value that already contained
    ${needs. is now replaced; no stock starter does.

  • AppTown keeps its own contract, not Score. The decision
    (design/decisions/app-contract-and-score.md) takes Score's Kubernetes
    units and value wiring, and says what would make AppTown export, import or
    adopt Score. A Score exporter or importer would not change the 1.x
    contract.

  • One architecture per AppTown. The app contract says it: workers and
    targets run one CPU architecture, a release to another is refused before
    anything is built, and a project moved to another architecture builds its
    releases again.

Needs

  • A need's default version is fixed for 1.x. A need declared without a
    version gets PostgreSQL 16, MariaDB 11.4.13 or Redis 7 in every 1.x
    release, so an app is never moved to a newer server on its old data.
    Write version: to choose another.

  • MariaDB starts reliably. Its first start used to give up on a busy
    host after about 30 seconds and leave the app without its database. The
    need's start script now sets up the data folder itself, and at every start
    makes the app's database and user if they are missing. A first start takes
    about 3 seconds. A standing MariaDB failed from its second start on most
    Linux hosts, and a small one was killed on its first start. Both are
    fixed. Passwords and database names with quotes or backslashes reach the
    server as written.

  • A service Docker is restarting is waited for, within the same time
    limit, instead of being failed as unhealthy.

  • A need takes only the parameters its catalogue lists, plus
    disposable, size and resources. The properties: pass-through is
    refused, and kind, prefix and as are reserved for naming two needs of
    one kind later.

  • AI models (llm, Experimental) give LLM_API_KEY and LLM_BASE_URL,
    an OpenAI-compatible chat-completions API; OpenRouter answers it today.
    The OpenRouter-named variables are gone.

  • Error reporting gives SENTRY_DSN only. GLITCHTIP_DSN and the
    Spring-only setting are gone (the Spring setting is in the java-spring
    guidance).

  • Object storage (s3) is a private store with an S3 API, not public
    file serving: no public address, and presigned URLs are outside the
    contract. Serve objects through the app.

  • Words: a need is what the app declares; a service is what a need
    gets in a deployment or preview; a need is fulfilled by the target or
    through an integration. Settings › Services lists every need and says what
    fulfils it. Error reporting is an integration, not a need.

  • One contract per need, the same on every target. Each stock need
    lists what it gives the app under provides:, and the services-and-data
    guide has the table (a test holds it to the catalogue). "What 1.0
    promises" promises those names through 1.x. A contract is refused at its
    check, at release admission and when a preview boots if it puts an
    integration such as errors under needs:, sets a password AppTown sets,
    or selects two needs that give the same variable. Redis gives REDIS_URL
    only, and MariaDB adds DB_SSL_MODE (DISABLED or REQUIRED).

  • A need that is not healthy fails the boot. On a Docker host the boot
    used to start the app beside a need whose health check was failing. Now it
    stops before any init step or part starts and names the need. A database
    that is still initialising counts as starting until the boot's startup
    limit runs out, so a slow first start is not refused.

  • Object storage on App Platform. An app that needs s3 gets a Spaces
    bucket of its own and a key that reaches only that bucket. AppTown makes
    the bucket with a short-lived key that it deletes straight after (and
    deletes one an interrupted release left behind). App Platform's stock needs
    are postgres and s3.

  • Each deployment has its own need credentials. On a Docker host, on
    Portainer and in a preview, each deployment and each preview has its own
    password for Postgres, MariaDB and Redis, and its own keys for object
    storage. Before, every deployment used the catalogue's fixed values (app,
    local/localsecret) and Redis had no password. Redis's password is in
    REDIS_URL. AppTown derives the values from its secret store's key and
    does not store them, so anyone who holds AppTown's database can work them
    out, as they can read the built-in store's values. A failed boot's kept
    output has them replaced with [secret]. The servers AppTown starts beside
    its own steps (tests, agent steps, apptown dev) keep the fixed values.
    App Platform keeps the credentials DigitalOcean makes.

  • A deployment's Postgres user owns its database and nothing more. On a
    Docker host, on Portainer and in a preview the app's user used to be the
    server's superuser. It is now an ordinary login that owns its database, its
    public schema and everything in them, and can use the extensions Postgres
    marks trusted (pgcrypto, citext, pg_trgm and the like). It cannot
    create roles or databases. The server's superuser is postgres, reachable
    only over the container's own socket, and no other role may connect to the
    app's database. App Platform already worked this way. The servers beside
    AppTown's own steps keep the app's user a superuser, because Django's test
    runner makes its own database and Rails loads fixtures with superuser
    commands.

  • The s3 bucket exists before the app starts, and the app's key cannot make
    one.
    On a Docker host, on Portainer and in a preview the store makes the
    bucket app and the app's account before it reports healthy. The store's
    root key is AppTown's and is never given to the app. An app that creates
    its bucket at start gets a 403 there, as it would on Spaces.

  • WordPress uploads go to the deployment's own s3 need. A new
    wordpress-bedrock project asks for no S3 settings, and the six settings
    under Settings › Secrets are gone. Media URLs are the site's own
    (/app/uploads/...), the site serves them from a private bucket, and no
    object carries an ACL. Media is backed up with the database. Docker host
    and Portainer only, since App Platform has no MariaDB.

Backups and restores

  • Restore rehearsals run on a small install. A rehearsal used to need a
    Docker host set aside for it and a backup store. When no host is kept for
    rehearsals, it now runs where the app's data is: on the app's own Docker
    host, or on an App Platform target's copy runner. It reads the backup from
    the backup store or AppTown's own disk and checks its size and digest. The
    record says which host it ran on, and the Data protection page says "not
    kept for rehearsals" when it ran beside other things. There it has its own
    containers, network and volumes and publishes no port, but it shares the
    host's CPU, memory and disk, and its outbound network is not blocked. An
    install that marks a host for rehearsals runs every one there, and a
    rehearsal is refused when that host does not answer. Rehearsals run only
    when a person asks.
  • A restored copy is always a restore test. AppTown starts it, checks it
    and takes it down. Opening and extending a restored copy are removed.
  • Object storage is backed up through the S3 API. On a Docker host the
    copy used to be a tar of the gateway's files, which lost each object's
    content type and metadata, and a restore said "files copied, not verified".
    The copy reads every object with its headers. A restore puts every object
    back, deletes the objects the copy does not have, and checks keys, sizes,
    headers and bytes, so it is verified. Object ACLs, tags and versions are
    not copied, restored objects get new modification times, and an object
    uploaded in parts comes back with a different ETag. An object over 5 GiB is
    refused. Copies taken before RC6 still restore as files, "not verified". On
    App Platform the bucket is copied on the copy runner with the deployment's
    own key, and its copy is checked by filling a new S3 store on the runner.
    Starting from a copy, a restored copy and a restore test fill the new
    deployment's own bucket. A copy taken before RC6 holds no objects, so it is
    refused for an app that needs s3. A take-down deletes the bucket only
    after a good copy whose check passed; otherwise it deletes the key, keeps
    the bucket and says why.
  • Portainer deployments are backed up. Before, AppTown copied nothing
    on Portainer and kept every volume at take-down. It copies a Portainer
    deployment's data with the same plan and commands as a Docker host's,
    nightly and after each intranet release, through Portainer's Docker proxy.
    The target's API key goes in no file, argument or environment variable.
    AppTown records the volumes its deploys made. A take-down deletes those
    after a complete final copy and keeps any other volume the stack uses
    (copied, not deleted). An environment AppTown cannot reach, or an edge
    agent in async mode (which has no Docker proxy), is refused by name, and a
    take-down there needs --without-final-copy. Restores and final copies on
    Portainer and on Docker hosts stop only the parts that are running and
    start exactly those again, so a part someone stopped stays stopped.
  • A need's storage survives its service being remade. When a release or
    redeploy recreates a need's service it keeps the same volume, or refuses.
    After a release AppTown compares the need's data with what it held before
    and says if it shrank.
  • A release over data AppTown cannot copy needs a person's acceptance.
    A release of new code onto a deployment holding data AppTown cannot copy
    now is refused at both release doors and says why. The cases are a
    held-data plan that cannot be read, an item AppTown cannot copy, an App
    Platform target with no backup store or copy runner, and a Portainer
    environment AppTown cannot reach. A person can release anyway with
    apptown release --accept-uncopied-data (acceptUncopiedData in the API),
    and the release's first log line records who did and why the data could not
    be copied. Redeploying the bundle a deployment already runs is not asked.
  • A restore runs as an operation. Restoring a backup set (from the
    Backups page or the API) answers at once with the operation, which
    apptown wait operation and the operation's page follow, with each step
    and each item's outcome as it finishes. If AppTown restarts during a
    restore, the restore ends unknown with what it had confirmed, and
    AppTown does not start the app again. The record names the containers the
    restore may have stopped. The restore may still be writing on the host, so
    check that no restore container or pg_restore is running and look at the
    containers, then release the deployment again. The safety copy holds what
    was there before. apptown executing lists running operations as well as
    steps, so a deploy that waits for it also waits for copies, restores and
    take-downs.
  • Backup checks are kept per item. A deployment's data view (its card and
    apptown data) used to show every item with the newest check's result, so
    a database could show a bucket's check, with "came back ... as" and nothing
    after it. Each check records its item, and the view says "came back on
    from into ", with "as " only when there is one.
    A database's place reads "a new database on the deployment's cluster", and
    a bucket's names the copy runner. The weekly check of production data is
    due when any item's own last check failed, is missing or is a week old.
    The "not assessed" line for outside data reads "data the app keeps
    outside the needs it declares, such as a bucket or a database it reaches
    with its own settings", so it no longer contradicts a covered s3 need.
  • Starting from a copy needs every item checked. The first release of a
    deployment that starts from a copy is refused unless every item it would
    fill passed its last check of that copy. The refusal names the item and
    its last check. A person can go on with
    apptown release --accept-unchecked-copy (acceptUncheckedCopy), and the
    release records it. A take-down checks its final copy as part of it, so a
    whole final copy starts as before; one whose check failed for an item
    needs that choice. "Checked" beside a final copy means every item came
    back.

Deployments and releases

  • A need's storage is kept when its service is remade, or the release is
    refused. Before a release AppTown checks it can copy the data it holds
    (apptown release --accept-uncopied-data releases anyway, recorded);
    after it, it compares the need's data with what it held before.
  • A stopped need is started, not just judged. A deploy that reuses a need container which has stopped starts it (same container, same storage) before waiting for it; if Docker cannot say whether it runs, nothing is started. A need that is not running is reported as not running, not as unhealthy.
  • A failed release on a Docker host ends failed, so the next release or
    a take-down can follow. A boot that failed after it started changing the
    host (a migration that exits non-zero, a service that never gets healthy,
    a readiness check that does not pass) was recorded as uncertain. Nothing
    could settle it, and every new release and take-down was refused. Now any
    failure the boot reports ends the attempt failed, on both the release and
    the landing path. A failure does not undo what the boot changed. Releasing
    again replaces the failed app container and reuses standing services and
    their data. When AppTown loses track of an attempt, Check again settles it
    from what the host says. Only a host that cannot be asked leaves it
    uncertain. One case reads wrong, and the deployment guide says so: if
    AppTown stops after a healthy boot but before writing its record, the
    attempt reads failed while the new container runs. The next release
    replaces it.
  • A release built on AppTown's own Docker is published. An AppTown with
    no worker builds a landing's release on its Docker host. It recorded those
    images by local id and never pushed them, so releasing them to Portainer
    or a bucket website failed. Now, when the project has a container
    registry, the landing publishes each image there, as a worker's build is,
    and the release records it by digest. If the registry refuses, the step
    says the new version is running and was not published. Releases recorded
    before this are not published later: land a new change.
  • A worker changes where a job runs, not what it produces. Intranet
    releases (apptown release <project>) no longer build and push images
    themselves. They ship only a release a landing published to the project's
    registry, and refuse one that was not, at the request. Before an agent
    step, AppTown merges main into the change on its own machine as well as on
    a worker, and tells the agent. If an uncommitted file blocks the merge, the
    step stops before the agent starts. An agent's private Docker is not
    logged in to any registry, with or without a worker.
  • Portainer gets the integration settings a Docker host gets, such as
    SENTRY_DSN, FEEDBACK_URL and REGISTRY, on the public service only.
    The app's own environment and secrets win. The release log names the
    settings but not their values.
  • Portainer deployments report whether they run. AppTown reads each
    Portainer environment's app containers once per sweep. A list it could
    not read shows unknown, never stopped.
  • A preview restarts when its secrets change. A preview asked for again
    with the same commit kept running with the old secret and integration
    values. The comparison now includes each secret's name, store and version
    and a keyed fingerprint of the values, never the values themselves. Every
    running preview starts again once, the next time it is opened.
  • A fix-forward can land past a merged change whose deploy failed. The
    earlier landing no longer holds the project's landing slot once its merge
    is recorded; when the later one ships, the earlier change reads "shipped
    with a later change". A recorded merge is final: a landing never merges
    twice.
  • Releasing to a Portainer deployment by name works, with its needs.
    apptown release <project> <deployment> and a card's Deploy latest
    failed for every Portainer deployment ("portainer renders no artifact of
    its own"). They build the same stack as the intranet release, needs
    included. They refuse an image that was not published to the project's
    registry by digest, an environment whose platform is unknown or differs
    from the bundle's, and a second deployment of one project on the same
    Portainer target.
  • The Portainer environment is a setting of the target. The intranet
    target's environment is set under Edit target on its Infrastructure page.
    A new install starts with local. Before, it was always prd-forge, and
    AppTown set it back at every start.
  • A $ in a value reaches Portainer and apptown dev as written.
    Secrets, contract environment values, data folders, build contexts and
    images in the Compose files AppTown writes used to be read as variables, so
    pa$word arrived as pa.
  • A value App Platform would change is refused before the deploy. App
    Platform replaces ${NAME} in any environment value, secret or not, when
    NAME is one of its own variables, and has no way to escape it. A deploy to
    App Platform stops before anything is made when a secret or declared
    environment value contains ${…} (other than a ${needs.…} reference),
    naming the variable and the part. Before, the app silently got the changed
    value.
  • Only main is served at a project's address on an attached Docker
    host.
    On a Docker host attached with an ingress domain, main is served
    at https://<project>.<domain>. Any other deployment of the project, such
    as staging or a fresh copy, is shown at the host's address and its own
    port, http://<host>:<port>, which people reach only if the host's firewall
    lets them. Before, such a deployment was given main's address too and took
    about half of main's visitors. Addresses of main, the built-in host,
    Portainer and App Platform do not change.
  • Every deployment on a Docker host keeps one port. A deployment keeps
    the host port it got at its first start, whether a landing or a release
    started it. Before, a deployment other than main that had been released
    only once got a new port from Docker every time its container started (a
    restore, a health restart, a host or Docker restart). AppTown kept probing
    the old port, so it restarted the deployment again and again, and the card
    showed an address that did not answer. Previews still take a port Docker
    picks, and AppTown asks Docker for it each time.

Changes and checks

  • A landing's secret scan reads every commit it adds to main. The start-up
    step that set up the stock scanners dropped gitleaks' history command, so
    every landing scanned only its final files. A token committed and then
    deleted in the same change was merged into main's history. Now a finding
    in any added commit counts. AppTown reads the commit range itself and
    treats a range it cannot read as an error, because gitleaks reports that
    as "no leaks found". The scanner guide says what to do when a change has
    committed a secret and later deleted it.

  • A person can fix a blocked landing without a model. For a change no
    agent works on, apptown sendback stops the landing and puts the change
    back in progress, so its person can push a fix and land again.

  • Chats are refused when no model is set up, with the same sentence
    workflows give. Before, the chat opened and every message failed.

  • A change starts when it is opened. "Add to backlog" and the proposed
    state are removed, because a proposed change could not be worked on. An
    idea for later belongs in the team's ticket tracker. A request for a
    proposed change is refused. A proposed change left on an install is closed
    as abandoned by migration 165.

  • A step gets private Docker only when it declares docker: true. At
    every start AppTown used to add private Docker to any library workflow
    whose step used the implementation skill, and publish that as a new
    version. A workflow now keeps the text it was saved with.

  • Licence scans no longer fail beside the vulnerability scan. Both trivy
    scans shared one on-disk cache, which trivy lets one process use at a
    time, so the licence scan sometimes gave up and blocked the landing. The
    stock trivy scanners keep their scan cache in memory. The vulnerability
    database is still cached on disk.

  • Large Git pushes are accepted. AppTown's Git endpoint refused the
    chunked uploads Git sends for pushes over 1 MiB, so apptown check and
    change-ticket pushes failed for commits with large files. Each push is
    limited by -import-bundle-max-bytes (2 GiB by default). A larger one gets
    HTTP 413, and apptown check says the commit is larger than this AppTown
    accepts.

  • An approval counts people by their accounts. A vote AppTown cannot tie
    to an account does not count.

  • A stage with missing evidence cannot advance. Every stage records the
    checks it expects when it opens. apptown recheck-stage and "Run the
    checks again" are removed, and a run whose stage is missing evidence says
    it should be abandoned. apptown recheck for a landing's failed gates now
    names only the gates that failed.

  • Workflow rules are exact. A step's failOn must be low, medium,
    high or critical. A scan step with no failOn fails on every finding
    it may gate on; a custom workflow that relied on an image scan's quiet
    high should write failOn: high. A pack version may only name guidance
    blocks, workflows and policies that are in the library, so it pins each
    one.

  • Accept an adoption in advance. When you adopt a version from a what-if,
    you can say yes up front: "accept it if it changes no code and every step
    and check passes, none excused". AppTown casts that yes as yours, with the
    sentence you were shown, only when it has witnessed the condition; anything
    less waits for a person. An approval offers this only when its workflow
    declares what an advance yes means (inAdvance); stock quick-change,
    implement and land do. Agent tokens, connected apps and chats cannot give it.

  • A landing starts its own preview when a check needs the app running and
    no preview is proven to run the commit being landed. If the preview cannot
    start, the check says "could not check" with the reason and holds the
    landing.

  • A change can repair an app whose .apptown/app.yaml AppTown rejects.
    The agent step runs without the app's services and is given the error as
    the job; tests, previews, landing and release still refuse the bad file.

  • Adopting from a what-if starts the adoption's run, can move paired packs
    together and the project's other workflows to their newest versions, and
    says when a rule moves back to an older version. What-ifs have their own
    list, and a project's result is a score rather than a bare "could not
    assess".

  • Approvals show screenshots of the change's preview (desktop and phone),
    taken at the change's commit.

  • The boot check covers the home page, not only the health address, and
    stock PHP packs no longer display errors to visitors.

  • An adoption that needs no code change goes through without an override.
    When a project adopts a new way of working whose checks already pass, the
    agent step passes with "nothing to change" and says why, a person accepts
    it as usual, and the landing (the step that publishes accepted work)
    checks the new acceptance checks before the project moves over. Nothing is
    merged. Before, a person had to override the agent step. An ordinary change
    whose implementing step changed nothing still fails "made no change". The
    change's brief shows what each new check expects, and the reviewer accepts
    an empty diff when the code already meets the request.

  • A landing whose branch is already on main is refused when main moved
    after its checks.
    It used to record main's newer head as its result. It
    is refused with "the default branch moved since this landing's checks",
    as a new merge is.

  • Skipped tests are shown. When the test command writes a JUnit XML or
    go test -json report to $APPTOWN_TEST_REPORT, the tests check records
    how many tests passed, failed and were skipped. Go projects on the detected
    command get this with no change. A pass with skipped tests, or one whose
    counts are not known, reads "passed with notes" and says which. The exit
    code alone still decides pass or fail.

  • Approvals name the credential they came through, for example "approved
    by sam through CLI token release-bot", on the change page and in
    apptown trace.

Trying and installing

  • Try AppTown with one command.
    curl -fsSL https://apptown.dev/try | sh starts AppTown in one Docker
    container on your own computer and opens it; sh -s -- stop and
    sh -s -- uninstall stop it and remove it. The script is in the
    repository's site/ folder, to read before running. It runs its own
    Docker inside the container. The link it opens carries the one-time setup
    token, so the token is in the container's log until the administrator is
    created. To support it, an AppTown whose -agent-image names a registry
    pulls that image when it is missing instead of building it, and a tier
    domain may carry a port; before, such a domain never matched.

  • The desktop trial is HTTPS too. With apptownd -local-tls (on in the
    trial), AppTown makes its own certificate authority, limited to
    localhost names and the computer's own addresses, and serves itself and
    every app at https://…localhost:7777, so apps run as they do on a server.
    On macOS the try script explains why and asks before adding the authority
    to your login keychain, and uninstall removes it; on Linux it links the
    steps for the system store, Chrome and Firefox. Chats on the trial trust it
    already. Windows is not supported yet. AppTown's checks that open an app's
    preview trust this authority too. Before, they failed with "certificate
    signed by unknown authority", which blocked every first build on the trial.

  • Extra certificate authorities, for a company's TLS-inspecting proxy.
    Some networks re-sign every outgoing HTTPS connection with a company
    certificate authority. Three apptownd flags each take a PEM file of extra
    authorities, trusted beside the system's own:

    • -trust-inference (APPTOWN_TRUST_INFERENCE): model providers.
    • -trust-deployments (APPTOWN_TRUST_DEPLOYMENTS): apps' preview and
      deployment addresses, which AppTown's checks open, and container
      registries.
    • -trust-apptown (APPTOWN_TRUST_APPTOWN): everything else AppTown
      calls, such as Git hosts, integration providers and cloud provider APIs.

    The same file can be given to all three. A file that is missing or holds
    no certificate stops AppTown at start, naming the flag. With
    -trust-apptown, git is given the system's roots and the file together,
    so the system must keep its roots in one file, as Linux distributions and
    macOS do; otherwise AppTown does not start. A worker takes
    -trust-deployments and -trust-apptown. Setting up a server explains
    them. These flags are not yet in the list "What 1.0 promises" keeps
    through 1.x.

  • The containers AppTown starts do not trust these authorities yet.
    Agent, check and build steps, previews and deployed apps are not given
    the files these flags name, so behind such a proxy their own HTTPS
    connections still fail. RC7 adds this. A host's Docker trusts what that
    host's system or /etc/docker/certs.d trusts.

  • One server with one compose file. The ghcr.io/flokkit/apptown image
    has a one-server mode, and the repository ships a compose file that runs it
    with Caddy in front for HTTPS. Model access has default addresses on a
    one-server Linux install.

  • Release archives. Each release publishes archives of the binaries for
    installs that do not use the images.

  • Supported platforms and system requirements are a guide of their own:
    Linux servers (amd64, arm64); the desktop trial on Apple Silicon Macs and
    Linux; current Chrome, Edge, Firefox and Safari; memory, CPU and disk
    measured on a real trial.

  • Running AppTown is a short guide. It gives three ways to install (the
    desktop trial, one server, and a split install with AppTown alone, agents
    on a worker and apps on attached Docker hosts or a cloud), which one suits
    whom, and links separate guides: Setting up a server, Split install,
    Deployment targets, People and roles, AppTown's own backups, Deployment
    backups, Upgrading, and Monitoring.

  • Release images are built for amd64 and arm64. The release workflow's
    merge step joined other images' digests into the server image's tag; it
    keeps each image's apart now.

  • The apptown image as a server. Set APPTOWN_NO_LOCAL_DOCKER=true in
    its env file. Without it the image starts as a desktop trial, which needs
    --privileged.

  • apptown.dev serves the project's page, the try script, the one-server
    compose.yaml and a tour of the web interface. A release tag publishes them after its images, from
    GitHub Actions with a role that can only write that site. The page shows
    both ways to start: the one-command trial for your own computer, and
    "Run it on a server" with the one-server compose file's three commands,
    the DNS names and ports to set up first, and a link to Running AppTown.

  • A tour of the web interface. make tour photographs a small example
    team's AppTown, page by page, in light and dark. The screenshots are
    committed with each release (the release script refuses one whose tour is
    out of date) and published at apptown.dev.

  • The source is public. github.com/flokkit/apptown is where the code
    changes, one squash-merged pull request per change. It has a README
    written for the administrator who sets AppTown up for others,
    SECURITY.md (how to report a vulnerability, and what to know before
    sharing AppTown) and CONTRIBUTING.md. Every pull request runs the web
    checks, the Go checks, the unit tests and a check that nothing private is
    committed. The Docker integration tests, journeys, browser tests and race
    detector run after each merge, and a tag's images and archives are built
    only when they passed on that commit. For anyone running make check on a
    Mac, the gate refuses to start under macOS Game Mode.

  • How to check a release. Setting up a server has a section with the
    cosign verify command for the images and cosign verify-blob for
    SHA256SUMS, each tied to the release workflow for that tag. A release
    made while the repository was private has no signature.

  • AppTown's own build runs no npm install scripts, and every base image
    in AppTown's own Dockerfiles is pinned by digest. CI checks both.

First run and the web interface

  • The first-account page works for a new install. It names AppTown, and
    its "Running AppTown" link opens the guide on GitHub; before, the link led
    to the built-in docs, which need a signed-in user.
  • A first project starts building. "Build the first version" now waits
    until the chat's helper is running before it sends the opening message;
    before, the message was typed while the chat was still installing its
    toolchain and never sent. A chat shows "your turn" only when the helper has
    finished, as the server records it, not while it runs a command.
  • A first model key gets a model that exists. An Anthropic key's default
    is the newest Sonnet in Anthropic's own model list (it was
    claude-sonnet-latest, which Anthropic does not have, so every chat
    failed); an OpenRouter key's is what OpenRouter's latest-Sonnet alias
    names.
  • apptown new without a pack says what to do next. Such a project has
    no way of working, so it cannot take a change. Before, the command
    suggested apptown quick, which was then refused. Now it says to apply a
    pack with apptown pack -p <slug> <pack> and lists the packs this AppTown
    offers.
  • Smaller first-run fixes. The new-project questions are yes/no
    questions; a starter that already declares Postgres says so instead of
    "services skipped"; the new project's bindings no longer all show a green
    tick.
  • Secrets integrations say what is set up. An Infisical integration on
    an AppTown without Infisical settings reads "not set up" instead of
    healthy; a configured one is checked with a real read, and the built-in
    store by its key. A project's card and each run's record list only the
    secrets stores that are available (before, every one named Infisical).
  • Page help. Pages that opened with a dense paragraph show the title,
    one short line and a "?" beside the title. The "?" opens the explanation
    and a link to the guide. It works from the keyboard and on a phone.
  • Home shows only what needs you. "Needs you" lists an operation you
    asked for only when it failed or its outcome is not known. The Home
    sentence counts the same list ("1 needs you"), and says "All quiet" only
    when it is empty. Runtime badges show the state alone while it is
    current, and the time it was read only when it is out of date. Project
    cards with no deployment show no running or stopped badge.
  • The web interface offers only what exists. The release button shows
    only where an intranet target is set up; a queued conformance review shows
    no spinner or unknown total; pack cards name only missing integrations;
    one search box in the top bar on every page, also in the narrow menu; a
    failed read of a release plan is shown as failed.

Agents

  • A smaller agent image. The agent image is 1.73 GB (2.66 GB in RC5,
    arm64, measured 18 October). Its browser is Chromium's headless shell, and
    there is no chromium command any more; the chat briefing names the new
    one. It no longer contains Go. A Go project uses the Go its mise.toml
    declares, as its steps and chats already did, and the clean build of a
    merge-only release does the same. A Go project that declares none is told
    to pin one. The scanners use semgrep 1.99.0, uv 0.13.0, Node 24 and Go
    1.27.0, so a project on an older Go may be asked for formatting or vet
    fixes Go 1.27 wants.

Smaller fixes

  • AppTown acts as apptown everywhere: its scheduler, webhooks and system
    operations (new records say apptown-system; old records keep what they
    said), the chat placement, telemetry attributes (apptown.*; the dashboard
    is apptown-dashboard.json), and the project it reports its own errors to
    (apptown; an install that made a forge project should rename it).

  • Settings › Services tiles are short: a badge for experimental kinds, one
    line, and what fulfils it. A dependency kind may declare summary and
    status: experimental.

  • apptown diff <change> shows that change's code (project#n or its
    id), as show does; a run's diff is apptown diff -r <run>. Before,
    snake#7 was read as snake's run 7.

  • "This change adds" lists what the change declares even when main's
    .apptown/app.yaml cannot be read, and still shows what may be removed.

  • An AppTown refuses another installation's built-in database. apptownd
    refuses to start on a database volume whose apptown.instance label (or
    forge.instance on an install made before 1.0) is not the id in its data
    directory's instance-id, or that has no label. The message names the
    volume, the folder it points at and the docker volume rm command.
    Before, a fresh install used a volume left by an earlier one and failed
    later on its password.

  • A cancelled database query no longer breaks the next one. On a busy
    machine a cancelled query could leave its connection holding an error,
    and the next query on it failed. AppTown now checks such a connection
    before using it again, and replaces it if it does not answer.

  • The Infrastructure page lists a host's containers in one Docker call.
    It used one per kind, each a separate ssh connection, so a small, busy
    host ran past the page's time limit.

  • The image reaper keeps a freshly rebuilt conformance image. A rebuild
    from cache keeps the old creation time, so it looked a day old and was
    removed minutes later.

  • Evals. A project advanced past its pack can be copied for an eval. An
    arm whose run is blocked is failed with the reason, so the queued arms
    start. Adoption changes are re-run as adoption changes. An approval the
    eval settles names the commit it approves, so eval arms can land.

  • A step that has finished is not cancelled when its run ends. If a
    step finished just as its run ended, AppTown could treat the step as lost
    and cancel it. It now reads the run first, so only a step still pending or
    running in an ended run is cancelled.

  • The disk floor is at most 20 GiB. New work was refused below a
    twentieth of the disk free, which on a 3.6 TiB disk meant refusing with
    184 GiB free. The floor is now a twentieth of the disk, at least 1 GiB and
    at most 20 GiB.

  • Every check and every take-down with a final copy on App Platform failed
    in RC5, because a check's new database name was 46 characters and
    DigitalOcean allows 40.

  • A refused intranet release no longer saves the hostname it asked for; the
    hostname is saved once the release is admitted.

  • A release's record names the attached integrations its target does not
    deliver (Portainer and App Platform run the app without their values).

  • The state-behind-needs guidance check passes a repository with no app
    part, such as a command-line tool or library.

  • The static file server starts without warnings, and the S3 and
    DigitalOcean clients keep their own connections, so another component
    closing the shared pool no longer breaks their requests.

Running AppTown

  • Sign-in is the default. apptownd starts with -auth internal
    unless told otherwise. Open mode, where a caller names itself and there is
    no sign-in, needs -auth open. Every shipped install already set
    internal.

  • Settings an administrator changes are set in AppTown and need no
    restart. apptownd's flags hold only what AppTown needs to start and what
    only the machine's owner should decide:

    • -agent-model is gone. Choose the default model on Home and in
      Integrations (model slots). With none chosen, model work is refused
      until an administrator sets one up.
    • -agent-concurrency is gone. Each model provider's "Steps at once" in
      Integrations decides it. A provider AppTown cannot read runs one step at
      a time.
    • -errors-url is gone. Type the GlitchTip address in its form.
    • -mirror-base is gone. The package-mirror forms suggest the address of
      a mirror already added.
    • -portainer-environment is gone. The intranet target's environment is
      set under Edit target. An existing install keeps the name its target
      records.
    • APPTOWN_REAP=on is now APPTOWN_REAP=true (the flag -reap). The
      value on is refused at start, naming the variable.
    • -trust-inference, -trust-deployments and -trust-apptown are new
      (see "Trying and installing").

    A command line that still passes a removed flag stops apptownd at
    start, naming the flag. An APPTOWN_ variable for a removed flag is
    ignored. "What 1.0 promises" lists the flags kept through 1.x.

  • The CLI's exit codes. 64 means the command line is wrong (an unknown
    command or flag, a missing argument); nothing is sent. 69 means AppTown
    could not be reached or stayed unavailable through the CLI's retries.
    Before, a bad flag exited 2, which wait uses for "needs a person", and
    an unreachable AppTown exited 1, "failed". A refusal is still 1.

  • apptown wait shop#12 waits on change 12 of shop. Before, it waited on
    run 12. In wait, the shop#12 form and a bare number name a change; a
    run is named by its id.

  • A webhook token or apps domain set under a FORGE_ name is reported
    as missing, and the error asks for the APPTOWN_ name.

  • db.env must hold APPTOWN_DB_URL, APPTOWN_DB_PASSWORD,
    APPTOWN_DB_CONTAINER and APPTOWN_DB_VOLUME.
    A file missing any of
    them is refused with the missing keys named, and left as it was.

  • Tokens made before tokens had a purpose are revoked (migration 161).
    They were already refused when used. Every row is kept, with when it was
    last used.

Removed

These had no users, or existed only for records on the maintainers' two
installations, whose data was checked or migrated first.

  • Project export (apptown export-project and its API). Nothing could
    import an export.
  • The backlog ("Add to backlog" and the proposed state).
  • The dedicated check-only worker (-check-worker, the worker's -role).
  • The standalone apptown-check, apptown-deploy and apptown-console
    programs.
  • -drain-local-docker. To switch to no local Docker, stop AppTown, make
    sure no local work is left, and start it with -no-local-docker (the
    workers guide).
  • -features and the "Start clean" button on the Conformance page.
  • -backfill-recorded-main. Every project has its recorded main.
  • Creating, editing and promoting change-catalogue entries. The catalogue
    is stock/catalog.yaml, shipped with each release. Listing and applying
    entries work as before.
  • The workflow-drafting endpoint and its model slot, the "simple change"
    endpoint, and webhook triggers (on: webhook).
  • The one-time ownership inspection.
  • Routes nothing called: pack picking, revoking a project deletion, a
    library workflow's history, removing a member by name, listing secret
    stores, renaming a user, per-project extensions, moving a project to
    another group, and /items/{id}.
  • The same-machine probe behind the host-memory bar. The host's own memory
    is shown only for the built-in target, when its total matches Docker's.
    Elsewhere it shows as not known.
  • apptown recheck-stage and "Run the checks again" (see "Changes and
    checks").
  • Running a static-site bundle built before 3 October 2026 with the file
    server's command. Released again, such a bundle fails its readiness
    check.
  • The workflow schema's on: workItem trigger, which did nothing. Stored
    workflows that use ${{workItem.*}} still expand.

Migration 163 drops the tables and columns only these features used,
rewrites old backup items, need states and deployment readiness into their
current form, and revokes live grants of the removed permissions (the
revoked rows are kept).

Supported and experimental

  • Workflow options are checked when a workflow is saved. An option a
    step does not take is refused by name, with the ones it does take.
    Before, an unknown option was dropped: minApprovals: 2 on an approval
    saved, and one vote passed it (the option is quorum). Stored workflows
    keep running unchanged.
  • Packs and guidance blocks may state apiVersion: apptown.dev/v1, as
    workflows can; any other version is refused.
  • The compatibility guide lists what is Supported in 1.0 and what is
    Experimental, with what each Experimental item lacks. Experimental items
    say so at the start of their description: the java-spring,
    node-fullstack, wordpress-bedrock, dotnet, rust-service, godot-3d
    and video-game packs; the redis and llm needs; the Portainer, App
    Platform and bucket website targets; the research, excavate and
    product-spec workflows; and the customer feedback integration.
  • Newly experimental: the MCP tools at /mcp/user (a 1.x release may
    change them, and its notes say what changed), get_eval and
    get_what_if, the backup-set manifest, approvals given in advance
    (inAdvance), and bare apptown release <slug>, which releases to
    Portainer.
  • Newly stated in "What 1.0 promises": a field's listed values (a state,
    an outcome) may gain values; every core command except executing takes
    --json; for the MCP tools, the names, required arguments and
    descriptions are promised, not the fields of an answer; the sh check
    contract (a read-only copy of the project, no network, at most 60
    seconds, exit 0 conforms, 1 is a finding, anything else is "could not
    assess"); a pack's toolchain is a starter mise.toml, not pinned by the
    pack version; the integration variables an app gets; and /_apptown/
    belongs to AppTown on every app.
  • apptown help lists the commands promised through 1.x first, now
    including apptown executing, and the rest under "not promised in 1.x".
  • -agent-executor local and -host-scripts say in their help that they
    are experimental.

Upgrading from RC5

There is no supported upgrade from a release candidate. RC6 is for new
installs. The code that moved an install and its projects from the forge
names (apptown upgrade) was removed before this release, once the
maintainers' two installations had been moved.

An RC5 install cannot simply be started on RC6:

  • AppTown reads no FORGE_* setting, and db.env must already use the
    APPTOWN_DB_* keys.
  • Apps get only the APPTOWN_* names and /_apptown/.
  • Projects' .forge/ folders are not moved to .apptown/.
  • Sessions, MCP tokens, invite links and Git tickets made before the rename
    stop working.

The database itself does upgrade: RC6's migrations run over an RC5
database, and the upgrade tests check it.

For a new install, follow Running AppTown. For later upgrades, the
upgrading guide is unchanged: check nothing is executing, stop AppTown,
take the two copies, replace the binaries and start.

Database migrations

RC6 has thirteen, 153 to 165. 153: each backup check records the item it
checked. 154: stored names the rename changed (backup sets on AppTown's own
disk are apptown-disk, complaints about the platform are about
apptown). 155: the stock platform pack is apptown. 156: a what-if
records whether it moves each project's other workflows to their newest
versions. 157: a what-if records the other pack versions it moves with its
own. 158: each deployment records the prefix of the names it gives things
outside AppTown. 159: a change can be closed as shipped with a later
change. 160: stored category values forge become apptown. 161: tokens
made before purposes are revoked, and a token without a purpose must be a
revoked one. 162: deployments are named on their hosts by their key, and
the prefix is kept only for one not yet moved. 163: the removed features'
tables and columns are dropped, and old deployment records take their
current shape. 164: pack versions that named a policy without its
definition get the library's definition. 165: a change left proposed is
abandoned.

HTTP API changes

Routes added:

  • POST /api/v1/projects/{slug}/deployments/{name}/move-names: move a
    deployment made before RC6 to the new names (apptown move-names).
  • POST /api/v1/changes/{id}/recheck: run a landing's failed gates again
    (apptown recheck).
  • GET /api/v1/conformance/what-ifs: the list of what-ifs.
  • GET /api/v1/projects/{slug}/adoption/in-advance: what a yes given in
    advance would accept.
  • GET /tls/ask: a TLS proxy on the same machine, such as the one-server
    compose file's Caddy, asks whether a name may have a certificate. It
    answers only callers on the same machine, and only for names AppTown
    records.

Routes removed:

  • Project export: GET /api/v1/projects/{slug}/export.
  • The backlog: POST /api/v1/changes/{id}/start.
  • The ownership inspection: GET /api/v1/ownership/inspection,
    POST /api/v1/ownership/approvals.
  • The change catalogue's editing: POST /api/v1/catalog,
    PATCH /api/v1/catalog/{name}, POST /api/v1/catalog-promote/{name}.
  • Restored copies: POST .../restored-copies/{id}/open and .../extend.
  • Rechecking a stage: POST /api/v1/runs/{id}/recheck.
  • Webhook triggers: POST /api/v1/hooks/{slug}/{workflow}.
  • Drafting and simple changes: POST /api/v1/assist/workflow,
    POST /api/v1/projects/{slug}/changes/simple.
  • Feature switches: GET /api/v1/features,
    POST /api/v1/conformance/history/clear.
  • Routes nothing called: POST /api/v1/packs/pick,
    POST /api/v1/projects/{slug}/deletion/revoke,
    GET /api/v1/projects/{slug}/workflows/{name}/history,
    DELETE /api/v1/projects/{slug}/members/{grantee},
    GET /api/v1/secrets/stores, POST /api/v1/users/{name}/rename,
    GET and POST /api/v1/projects/{slug}/extensions,
    POST /api/v1/projects/{slug}/owner, and GET /items/{id}.

Responses changed:

  • The AppTown rename: request and response headers are X-AppTown-*
    (X-AppTown-Actor, X-AppTown-Deny-Code, X-AppTown-CLI-Protocol,
    X-AppTown-Version, …); fields named forge are apptown (capacity's
    figures and counts, a backup set's manifest, apptown version --json),
    forgeFault is apptownFault, the capacity tenant and a rehearsal's
    unavailability reason say apptown (on-apptown-disk), a complaint's
    about is apptown, and the OpenVEX document names
    apptown:project/<slug>. The API description's extension keys are
    x-apptown-*. A caller that sends the old X-Forge-* headers is served
    as one that states no protocol.
  • Restoring a backup set
    (POST /api/v1/projects/{slug}/deployments/{name}/backups/{id}/restore)
    answers 202 with the operation instead of a RestoreResult. The
    restore's outcome is the operation's result.restore.
  • Memory in requests and responses is in Mi/Gi: a deployment's memory
    override refuses a bare 512M, and resource views show 512Mi.
  • A request for a proposed change is refused.
  • A project's slug cannot be changed.
  • A request that passes a git push larger than -import-bundle-max-bytes
    gets HTTP 413.
  • Adopting a version (POST /api/v1/projects/{slug}/adoption/adopt) needs
    project:select-way-of-working, as applying a pack does, instead of
    project:configure.
  • GET /api/v1/packs now describes its answer, a list of PackView; the
    CLI reads it. The answer itself did not change.

Fields and values removed:

  • A new change's request (POST /api/v1/projects/{slug}/changes) has no
    propose, and a change's state is never proposed.
  • A deployment has no expiresAt (restored copies are no longer extended),
    and its readiness is never needs-reconcile.
  • A run (GET /api/v1/runs/{id}) has no needsRevision.
  • A check's placement has no role (the check-only worker is removed).

Request and response shapes gained optional fields:

  • A named release (POST /api/v1/projects/{slug}/deployments/{name}/deploy)
    and an intranet release (POST /api/v1/projects/{slug}/release) take
    acceptUncopiedData. apptown release --accept-uncopied-data sends it.
  • A named release also takes acceptUncheckedCopy, which
    apptown release --accept-unchecked-copy sends.
  • A backup item has bucket when it is an App Platform bucket.
  • The need catalogue (GET /api/v1/dependencies) gains provides,
    credentials, generate, integration and withdrawn; a need's local
    block gains entrypoint, configAtStart, keepsImage and stepEnv; a
    copy gains runIn. Its cloud entries lose terraform, inject and
    cluster.
  • A tests step's evidence carries testCounts and skippedTests, or
    testReport saying why the counts are not known.
  • POST /api/v1/dev-compose returns wired need values in its process-mode
    env.
  • A change (GET /api/v1/changes/{id}) gains head, the change's current
    commit; committedDiff.shipped, the release a change with no code of its
    own put live and the earlier release it was compared with; and evidence.inAdvance, a yes given in advance on an adoption.
    The rows of what a change adds gain baseUnread, saying why main's side could
    not be read when the change's could.

The CLI protocol (1) and the worker protocol (2) are unchanged; their
headers are renamed. A worker now refuses a request that does not say which
protocol AppTown speaks.

Deprecations

These are removals before 1.0, so no minor release comes first.

  • The forge names are gone, with no overlap. Apps get no FORGE_*
    variables, __FORGE_ENV__ or /_forge/ files. apptownd and the CLI
    read no FORGE_* settings, and the CLI does not read
    ~/.config/forge/credentials. Old invite links, Git tickets, MCP tokens
    and sessions stop working.
  • Five apptownd flags are removed (-agent-model,
    -agent-concurrency, -errors-url, -mirror-base,
    -portainer-environment), and APPTOWN_REAP=on is refused (write
    true). See "Running AppTown".
  • The features under "Removed" are gone, with their routes and flags.
  • A bare M or G for memory is refused in a version-2 contract. Write
    Mi or Gi. A version-1 contract is still read as before.
  • Stock that is no longer shipped stops being offered. A stock
    workflow, guidance block or policy this release no longer ships leaves the
    libraries and the workflow choices of projects that never selected it, and
    a stock pack it no longer ships is turned off. A project that already uses
    one keeps it, and its Working page shows "No longer shipped" beside it.
  • SQLite is no longer a stock option. The data/sqlite-everywhere
    guidance is withdrawn. Projects that adopted it keep their version; move
    the app's data to the postgres need and then remove the block.
    data/networked-database gets a new version whose text no longer offers
    SQLite.
  • The dynamodb need is removed. No stock pack used it, its emulator
    kept its data in memory, and no target but a Docker host could run it. Use
    Postgres (JSON columns) or Redis.
  • The sso and telemetry needs are removed. They were never
    fulfilled. Sign-in and telemetry values come from an integration attached
    under Settings › Integrations, which is unchanged.
  • A contract that declares dynamodb, sso or telemetry under needs: is
    refused as asking for an unknown need. errors under needs: is refused
    too; attach it as an integration.
  • Redis's REDIS_HOST and REDIS_PORT are withdrawn. Read REDIS_URL,
    which can carry the password.
  • A password under needs.postgres or needs.mariadb is refused.
    AppTown sets the password.
  • Removed workflows and policy. build, change-request and
    patch-dependencies (use implement, quick-change and
    dependency-uplift), and the mr-requires-secrets-scan policy. No project
    had them installed; past runs keep the definitions they ran under.
  • Unbuilt cloud promises are removed. The need catalogue loses the AWS
    blocks in postgres and redis and postgres's DigitalOcean cluster
    name, none of which were built.
  • The "Kubernetes cluster, not built yet" tile is gone from the
    add-a-target page.