Runlog docs

Start building

A plan buys a weekly allowance, sized for steady work.

Start

Working directory

A folder on your machine becomes a project that stays current. The loop is four calls: ask what the key opens and what is never read, price the reading before it happens, compare against what the project already holds, then push only the files whose content actually moved. Content addressing is what makes the second run cost nothing — an unchanged file is never re-read and never re-sent. Credentials are refused by shape rather than by folder, so a key that lands in a synced directory is left on disk instead of being indexed.

GET/v1/api/workspace/describeAPI key

Describe the binding

Ask which project this key opens, what reads as code, and what is never read.

Read this at run time rather than carrying your own copy. The skip lists and the per-file ceiling move with the reading pipeline, and a client that hardcodes them starts pushing bytes the server refuses.

`secretShapes` names the credential shapes no lane stores unasked. Skip them locally; a client that sends one anyway is refused per file rather than per push.

Request · curl
curl -X GET "https://runlog-7613480744.us-central1.run.app/v1/api/workspace/describe" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "codeExtensions": [
    ".go",
    ".ts",
    ".py"
  ],
  "maxFileBytes": 2097152,
  "projectId": "p7Kd2mQx",
  "projectName": "Checkout",
  "secretShapes": [
    "private-key",
    "cloud-credential"
  ],
  "skippedDirectories": [
    "node_modules",
    ".git"
  ],
  "skippedExtensions": [
    ".lock"
  ],
  "skippedNames": [
    ".DS_Store"
  ],
  "version": 1
}

Errors

StatuserrorWhen
401unauthorizedNo valid API key on the request.
GET/v1/api/workspace/manifestAPI key

List what the project holds

List every file this working directory already put in the project, under its content address.

Compare `contentId` against the same content address computed locally. Equal means the file is already read, and sending it again would cost money to learn nothing.

Request · curl
curl -X GET "https://runlog-7613480744.us-central1.run.app/v1/api/workspace/manifest" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "files": [
    {
      "contentId": "9dRk2mVt7QzLpXe4Nb1CsHgYo6JwFu3Ai0",
      "path": "cmd/main.go"
    }
  ],
  "version": 1
}

Errors

StatuserrorWhen
401unauthorizedNo valid API key on the request.
POST/v1/api/workspace/quoteAPI key

Price a reading

Ask what reading a given amount of new content will cost before any of it is sent.

This route is not metered. Asking what something costs can never be the thing that spends the allowance.

`basis` is `measured` when this project's own history priced it and `calibrated` when the platform figure did. `allowanceWindow` names the window `allowanceRemainingUsd` belongs to, so a quote never reports a figure against a window it did not come from.

Body

FieldTypeRequiredDescription
fileCountintegerrequiredHow many new files would be read.
byteCountintegerrequiredHow many bytes those files carry.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/api/workspace/quote" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "byteCount": 3819044,
  "fileCount": 412,
  "version": 1
}'
Response · 200
{
  "allowanceRemainingUsd": 6.15,
  "allowanceWindow": "weekly",
  "basis": "measured",
  "byteCount": 3819044,
  "estimateUsd": 0.42,
  "fileCount": 412,
  "prepaidBalanceUsd": 25,
  "version": 1,
  "weeklyAllowanceUsd": 9
}

Errors

StatuserrorWhen
401unauthorizedNo valid API key on the request.
POST/v1/api/workspace/syncAPI key

Push what changed

Send one batch of changed files and learn which of them landed.

A refusal is per file and never stops the rest of the push, so one unreadable file cannot cost you the batch. Every refusal names its path and says why in the words the person who ran the command reads.

`includeSecret` is you naming one exact path to read anyway. It is per path rather than a mode, so nothing sends a whole folder of credentials by setting one flag.

Body

FieldTypeRequiredDescription
itemsobject[]requiredThe files to send.
items[].pathstringrequiredPath relative to the folder root.
items[].contentTypestringoptionalDefaults from the path's extension.
items[].contentstringrequiredThe file's bytes, base64.
items[].includeSecretbooleanoptionalRead this one path despite its shape.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/api/workspace/sync" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "content": "cGFja2FnZSBtYWluCg==",
      "contentType": "text/x-go",
      "path": "cmd/main.go"
    }
  ],
  "version": 1
}'
Response · 200
{
  "indexed": [
    "cmd/main.go"
  ],
  "refused": [
    {
      "path": ".env",
      "reason": "This looks like a credential, so it was left alone."
    }
  ],
  "version": 1
}

Errors

StatuserrorWhen
401unauthorizedNo valid API key on the request.
402allowance spentThe window is spent and the prepaid balance is at zero.
POST/v1/api/workspace/reconcileAPI key

Drop what is gone

Tell the project which paths no longer exist on disk, so what it holds matches the folder.

Deletion is a separate call from the push on purpose: a client that crashed mid-scan must not be able to empty a project by reporting the files it had not reached yet as gone.

Body

FieldTypeRequiredDescription
pathsstringrequiredThe paths that are gone, relative to root.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/api/workspace/reconcile" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "paths": [
    "cmd/old.go"
  ],
  "version": 1
}'
Response · 200
{
  "refused": [],
  "removed": [
    "cmd/old.go"
  ],
  "version": 1
}

Errors

StatuserrorWhen
401unauthorizedNo valid API key on the request.