Attestable Builds

Attestable builds are a new approach to verifiable software distribution. Source code is compiled inside hardware-isolated environments that produce cryptographic proof linking binaries to their exact inputs.

POST /build/stream

Build with streaming progress via Server-Sent Events (SSE). This endpoint accepts the same parameters as POST /build but returns real-time progress updates instead of waiting for completion.

Request

All requests use multipart/form-data. You must provide either a source file OR a repo_url, not both.

Git Repository

curl -X POST -N https://api.kettle.build/v1/build/stream \
  -F "repo_url=https://github.com/owner/repo" \
  -F "ref=main"

ZIP Upload

curl -X POST -N https://api.kettle.build/v1/build/stream \
  -F "source=@source.zip"

Parameters

ParameterTypeRequiredDescription
sourcefileNo*ZIP file containing source code
repo_urlstringNo*Git repository URL to clone
refstringNoGit ref (branch, tag, or commit). Only used with repo_url

*One of source or repo_url must be provided, but not both.

Response

The endpoint returns a stream of Server-Sent Events. Each event contains a JSON payload:

Progress Events

data: {"type": "info", "message": "Initializing build environment"}

data: {"type": "info", "message": "Detecting project type"}

data: {"type": "info", "message": "Running cargo build --release"}

data: {"type": "info", "message": "Generating provenance"}

data: {"type": "info", "message": "Creating attestation"}

Completion Event

The final event contains the complete build result:

data: {"type": "complete", "result": {"build_id": "a1b2c3d4", "status": "success", ...}}

Event Types

TypeDescription
infoGeneral progress information
completeBuild finished, contains full result in result field

Client Example

const formData = new FormData();
formData.append('repo_url', 'https://github.com/owner/repo');

const response = await fetch('/api/build/stream', {
  method: 'POST',
  body: formData,
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value);
  const lines = text.split('\n');

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const data = JSON.parse(line.slice(6));

      if (data.type === 'complete') {
        console.log('Build result:', data.result);
      } else {
        console.log(`[${data.type}] ${data.message}`);
      }
    }
  }
}

Response Headers

Content-Type: text/event-stream
Cache-Control: no-cache
X-Accel-Buffering: no