HTTP app contract¶
Forge can build and run any HTTP app that follows the container contract below. The framework can be Node, Python, Go, Ruby, Java, or something else; Forge cares about the repo shape, the Dockerfile, the process it starts, and the serving probe response.
Requirements¶
| Requirement | What Forge expects |
|---|---|
| Dockerfile | A Dockerfile at the repo root that builds the app. |
| Manifest | A mithran.yaml at the repo root that declares the app, HTTP capability, startup command, and exposure. |
| Port binding | The app process listens on the port in $PORT. |
| Serving probe | The app returns HTTP 200 at the serving probe path Forge checks after runtime placement. The examples make / and GET /health return HTTP 200 so the app is easy to check locally. |
| Long-running process | The startup command keeps the HTTP process in the foreground. |
| Plain config | Non-secret config can go in non_secret_env; secrets do not belong in the manifest. |
If a deploy reaches the runtime stage but never becomes ready, check the
startup command, $PORT binding, Dockerfile, and serving probe behavior
together.
What is checked when¶
Two different checks protect the deploy path:
| Check | When it happens | What fails |
|---|---|---|
| Manifest review | Before build. | Missing or invalid mithran.yaml, unsupported fields, invalid capability shape, unsupported edge policy, or secret-looking non-secret config. |
| Runtime readiness | After the image builds and starts. | Process exits, listens on the wrong port, does not produce HTTP 200 at the serving probe path Forge checks, or starts a different command than the manifest declares. |
Use map --json status <deployment-ref> to tell which check failed. Review failures
show up as ReviewBlocked or review_status: Blocked. Runtime failures show
up as RuntimeFailed or runtime_status: Failed and may include
runtime_failure. Use deploy evidence and the app's local health check when
the status view is not enough to explain the failure.
Manifest capability¶
The HTTP capability tells Forge which service to start:
Use the startup command that starts your app inside the built container. If
map onboard creates a starter manifest for the repo checkout, review the
starter app ID, project/app ref, runtime, startup command, route, exposure, and
app-environment policy before the first release deploy.
The examples below show app and container patterns. Use the runtime value agreed for the app manifest.
Serving Probe¶
Runtime placement returns the serving probe path Forge checks before the deploy
can finish. mithran.yaml does not declare this path, and map --json status
does not expose it.
The probe response should be cheap and deterministic:
- return
200only when the app can serve traffic; - avoid calling slow external systems in the basic probe path;
- keep the response body small;
- make failures obvious in app logs and local tests.
The examples include GET /health for local checks, and the app root also
returns HTTP 200. A 404, 500, timeout, or process that listens on the wrong
port at the path Forge checks will keep the runtime from becoming ready.
Keep the app's local health check, startup command, and observed deploy status aligned before you publish the version.
Investigate probe failures¶
After a deploy reaches runtime placement, inspect the saved deploy status:
Read deployment.status.runtime_status and deployment.status.runtime_failure.
If runtime readiness fails and the detail points at health, confirm that the
container serves HTTP 200 on the same paths you tested locally, starts the same
command declared in mithran.yaml, and listens on $PORT. The exact probe path
is not a customer-visible status field; when local checks and deploy status do
not explain the failure, use the build-or-runtime readiness packet in
Support and escalation.
Startup command¶
capabilities[].startup.command is the command Forge records from the
manifest and uses for the app capability. It must be valid inside the built
container, not just on your laptop.
Good startup commands:
- start one long-running HTTP process;
- bind to
0.0.0.0; - read the port from
$PORT; - keep logs on stdout/stderr so build and runtime diagnostics are useful;
- fail fast when required non-secret config is missing.
Avoid startup commands that daemonize, fork into the background, wait for a prompt, depend on local files outside the image, or assume a hard-coded port.
Node HTTP example¶
const http = require("http");
const port = Number(process.env.PORT || 8080);
http.createServer((req, res) => {
if (req.url === "/health") {
res.writeHead(200, { "content-type": "text/plain" });
res.end("ok");
return;
}
res.writeHead(200, { "content-type": "text/html" });
res.end("<h1>Hello from Forge</h1>");
}).listen(port, "0.0.0.0");
FROM node:22-alpine
WORKDIR /app
COPY package.json ./
COPY server.js ./
ENV PORT=8080
CMD ["npm", "start"]
Matching manifest startup:
FastAPI example¶
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/")
def index():
return {"message": "Hello from Forge"}
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY main.py ./
ENV PORT=8080
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT}"]
For the manifest, set capabilities[].startup.command to the command the
container can run:
Local checks¶
Before deploying, run the same basic checks locally:
In another terminal:
Expected result:
/healthreturns HTTP 200;/returns the app response;- the container stays running until you stop it;
- the app listens on the port passed in
PORT.
Common mistakes¶
| Mistake | Result | Fix |
|---|---|---|
Binding to localhost only. |
The container starts but Forge cannot route traffic to the process. | Bind to 0.0.0.0 and $PORT. |
| Hard-coding a different port. | Runtime readiness fails even though the app works locally on another port. | Read process.env.PORT, os.environ["PORT"], or equivalent. |
| Serving probe returns a non-200 response. | Runtime never becomes ready. | Check runtime_failure, test / and the app's health route locally, and deploy the fixed app. |
| Startup command exits. | The runtime stops immediately after deploy. | Start the long-running HTTP server in the foreground. |
| Manifest command does not exist in the image. | Runtime fails at startup. | Align capabilities[].startup.command with the Dockerfile and installed dependencies. |
Secret committed in non_secret_env. |
Secret is visible in source control. | Remove it from the manifest and rotate the credential. |
For build and runtime failure troubleshooting, see Troubleshooting.