Skip to content

Admin guideBuilds

What a deploy does

The three phases of a deploy, what each can refuse, building from a folder, and what a failure leaves alone.

View as Markdown

Every deploy runs upload → scan → deploy. Each step belongs to one phase, and the step keys are a stable wire contract across the UI, CLI and API.

Phase What happens Can refuse on
Upload Clone the repository (or its folder), or unpack an archive. Dependency directories stripped; unsafe entries dropped. Source policy, archive caps, a clone credential, a bad folder
Scan Resolve what the app installs and compare it to the dependency policy. Severity, fixability, malware
Deploy Install dependencies, build, start the workload. Build timeout, language version, a start that produced no process

A refusal before anything is stored writes no deployment row. An upload that started and then failed is recorded, with the reason.

One repository can hold several apps. Each app names its folder:

Surface Where
UI New app → Folder (optional); later, the app’s Settings → Source & deploys → Folder
API repo_subdir on POST /api/v1/apps and PATCH /api/v1/apps/{id}
CLI toft create --repo <url> --subdir <folder>; toft status prints folder
  • Only that folder is deployed. The repository is cloned beside the deployment’s directory and the folder is moved in; nothing outside it and no .git is kept. Detection, toft.toml and the build all treat the folder as the app’s root.
  • Refused: absolute paths, . or .. segments, anything inside .git, and a folder that is a symlink in the repository (checked on disk, segment by segment). A missing folder, or a file where a folder is expected, fails the upload phase.
  • Without a name or slug, the slug defaults to the folder’s name.
  • An app deployed from an upload has no repository, so it has no folder.

Applied before the caps are counted, so a zip that is mostly node_modules usually fits once unpacked. .zip and .tar.gz go through the same unpacker.

  • A single top-level wrapper directory is unwrapped.
  • Dependency directories are removed.
  • Entries with absolute paths, paths escaping the root, or symlinks pointing outside are dropped.

What was dropped is reported on the deployment it changed.

  • Build memory, CPU and timeout are yours; a repository cannot ask for more.
  • Registry URLs and credentials come from the platform’s environment and override the app’s: an app declaring NPM_TOKEN gets the operator’s value. One allow-list governs which keys pass through, and a build credential never reaches the deployed app.

Off unless BUILD_ALLOW_SCRIPTS is set; a postinstall does not run. It is install-wide configuration, not a setting or a per-app choice: turning it on widens what every later build may execute.

A deploy in flight shows as a console above the app page’s tabs, with a phase bar and the build’s own output. The stored log is offered after the fact; the deployment list carries no log.

A build runs your organization’s code with the toolchain’s ordinary access. Mitigations: the source policy, the lifecycle-script switch, and per-app OS accounts (execution modes).