← One Black Cell
API

Drive One Black Cell from your own code

The one metered task, reading, is available over HTTP. It takes a rule number, the seed in words, the person's wish and the page's own facts about the universe (its estimate, density, spread, widest gap, centre column, twins, a documented resemblance and the eight one-law flips). It returns the behaviour the wish points to (fades, repeats, nests, churns or edge), a verdict (already, one_flip or no_flip), the law to flip, a note on the flip, a short field guide, two or three things to look for and a title.

The division of labour first: the page owns the universe (64 rows on a strip wide enough to act as an endless row), the behaviour estimate, every measurement, the eight flips and the verdict rule; the model owns reading the wish and all the wording; the page then re-checks the reply.

Base URL and the envelope

Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response uses one envelope:

{ "ok": true,  "data":  { ... } }
{ "ok": false, "error": { "code": "...", "message": "...", "status": 402, "details": { ... } } }

The token is minted for this app (the guest call takes {"slug":"one-black-cell"}), so no slug header is needed afterwards. Send it as Authorization: Bearer …. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required field (task, rule, seed, wish, page_facts) is missing.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: a reading is metered and needs a personal token.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field and the verdicts

Every run sends task: reading (the one task is reading); a missing or different one is still answered as reading. The model first turns the wish into one behaviour, wish_target. The verdict is then a fixed rule, applied in order: already when the page's estimate equals wish_target; one_flip when at least one of the eight flip lines shows wish_target; otherwise no_flip. For one_flip, pick_flip names the law whose flip gives that behaviour with the density closest to the current one.

Input fields (all strings)

FieldRequiredWhat it holds
taskyesAlways reading.
ruleyesThe rule number, 0 to 255.
seedyesThe seed in words: one black cell, or the name ADA spelled as 11 cells: 10010000001 (five bits per letter, A = 00001).
wishyesWhat the person wishes the universe did, or not given; over about 300 characters it keeps its start and end with the middle marked as cut.
page_factsyesOne key | ... per line: rule (with its eight laws, like WBB->B), seed, estimate, density, spread, widest_gap, centre_column, twins, echo, eight flip <LAW> lines and verdict_rule.
clip_notenoWhat the page shortened, if anything.
retry_notenoPresent only on the page's one reformat retry.

The reply

One JSON object, no prose around it:

{"task": "reading", "title": "...", "wish_target": "nests", "verdict": "one_flip",
 "verdict_reason": "...", "pick_flip": "WBB", "flip_note": "...", "philosophy": "...",
 "look_for": [{"fact": "widest_gap", "text": "..."}, {"fact": "density", "text": "..."}]}
FieldWhat it holds
wish_targetOne of fades, repeats, nests, churns, edge, read from the wish alone.
verdict, verdict_reasonThe verdict id by the rule above; one sentence of at most 30 words on what decided it.
pick_flip, flip_noteThe law to flip (three letters, e.g. WBB) or ""; one sentence of at most 30 words about the flip, or why none is needed or enough.
philosophyThe field guide: 40 to 80 words on what the universe does, built only from the facts.
look_forTwo or three {fact, text} items; fact is a fact key, text at most 25 words.
titleAt most 6 words.

The page then checks the reply (CellKit.check): the verdict against the rule for the reply's own wish_target, the named flip really giving that behaviour and being the closest in density, every look_for pointing to a real fact, every number in the prose being one the page supplied, no internal ids in the prose, and the word limits.

Worked examples, one per sample

These are the four free samples on the page: the exact input and a stand-in model's reply, unedited. Together they cover all three verdicts - rule 30 wishing for lace (one flip away), a 23 October birthday rule wishing for something wild (no single flip), the name ADA under rule 110 wishing for travelling shapes (already there) and the name JUNIPER under rule 184 wishing it would fade (one flip away).

Rule 30 from one black cell, wishing for lace - one flip away

Input:

{
 "task": "reading",
 "rule": "30",
 "seed": "one black cell",
 "wish": "Triangles inside triangles, like a piece of old lace",
 "page_facts": "rule | 30 - laws BBB->W BBW->W BWB->W BWW->B WBB->B WBW->B WWB->B WWW->W\nseed | one black cell\nestimate | churns - it never settles - busy, irregular, hard to predict; the page's estimate over 64 rows\ndensity | 53% of the cells inside the growing pattern are black\nspread | the left side moves 1 cell per row, the right side 1 cell per row\nwidest_gap | 11 white cells with black on both sides, at row 46\ncentre_column | rows 0-31 down the middle: 11011100110001011001001110101110 (18 black of 32)\ntwins | mirror twin rule 86; colour-swapped twin rule 135\necho | pigment bands on some cone-snail shells look strikingly like this rule's triangles; its centre column has been used as a source of random-looking bits\nflip BBB | W becomes B -> rule 158: repeats, density 64%\nflip BBW | W becomes B -> rule 94: repeats, density 53%\nflip BWB | W becomes B -> rule 62: repeats, density 63%\nflip BWW | B becomes W -> rule 14: repeats, density 3%\nflip WBB | B becomes W -> rule 22: nests, density 24%\nflip WBW | B becomes W -> rule 26: nests, density 18%\nflip WWB | B becomes W -> rule 28: repeats, density 27%\nflip WWW | W becomes B -> rule 31: fades, density 51%\nverdict_rule | already when the current estimate equals wish_target; else one_flip when at least one flip's estimate equals wish_target; else no_flip"
}

Reply:

{
 "task": "reading",
 "title": "The Churning One-Row Quilt",
 "wish_target": "nests",
 "verdict": "one_flip",
 "verdict_reason": "The current pattern churns and never settles, but flipping law WBB yields nesting triangles at a density closer to today's than the other nesting flip.",
 "pick_flip": "WBB",
 "flip_note": "Flipping law WBB turns this into rule 22, a nesting pattern of triangles within triangles, with density dropping to 24%.",
 "philosophy": "From a single black cell, rule 30 spreads outward, each edge creeping out one cell every row, while the middle churns through busy, unpredictable bands - about 53% of the growing quilt stays black. Watch row 46, where an 11-cell white gap sits bordered by black on both sides. This rule's triangle motif is said to resemble pigment bands on some cone-snail shells, and its centre column has even been used as a source of random-looking bits.",
 "look_for": [
  {
   "fact": "widest_gap",
   "text": "At row 46, spot an 11-cell stretch of white fully boxed in by black on each side."
  },
  {
   "fact": "density",
   "text": "The quilt runs 53% black - watch for that dark, speckled balance across the rows."
  },
  {
   "fact": "centre_column",
   "text": "Trace the middle column through rows 0 to 31: 18 of those 32 cells land black."
  }
 ]
}

A 23 October birthday rule, wishing for something wild - no single flip

Input:

{
 "task": "reading",
 "rule": "40",
 "seed": "one black cell",
 "wish": "Make it wild and unpredictable",
 "page_facts": "rule | 40 (from a birthday: day 296 of the year, wrapped past 255) - laws BBB->W BBW->W BWB->B BWW->W WBB->B WBW->W WWB->W WWW->W\nseed | one black cell\nestimate | fades - everything settles into flat rows of one colour (flat from row 1 on); the page's estimate over 64 rows\ndensity | 0% of the cells inside the growing pattern are black\nspread | the left side moves 0 cells per row, the right side 0 cells per row\nwidest_gap | no white gap with black on both sides\ncentre_column | rows 0-31 down the middle: 10000000000000000000000000000000 (1 black of 32)\ntwins | mirror twin rule 96; colour-swapped twin rule 235\necho | none\nflip BBB | W becomes B -> rule 168: fades, density 0%\nflip BBW | W becomes B -> rule 104: fades, density 0%\nflip BWB | B becomes W -> rule 8: fades, density 0%\nflip BWW | W becomes B -> rule 56: repeats, density 2%\nflip WBB | B becomes W -> rule 32: fades, density 0%\nflip WBW | W becomes B -> rule 44: repeats, density 2%\nflip WWB | W becomes B -> rule 42: repeats, density 2%\nflip WWW | W becomes B -> rule 41: repeats, density 49%\nverdict_rule | already when the current estimate equals wish_target; else one_flip when at least one flip's estimate equals wish_target; else no_flip"
}

Reply:

{
 "task": "reading",
 "title": "The One-Cell Stillness",
 "wish_target": "churns",
 "verdict": "no_flip",
 "verdict_reason": "The current estimate is fades, not wild and unpredictable, and every one-law flip leads only to fades or repeats, not churns.",
 "pick_flip": "",
 "flip_note": "No single law change reaches a wild, unpredictable pattern from this seed; try seeding with a name instead, or change two laws at once.",
 "philosophy": "A single black cell falls onto an empty row, and the pattern goes still almost immediately. Only two laws, BWB and WBB, turn white into black, and once the cell's neighbours turn white those laws stop firing, so every later row is flat. The seed never spreads sideways, and the centre column keeps just one black cell among thirty-two. Density sits at zero percent, and no gap ever opens between black cells, with almost nothing left to frame one.",
 "look_for": [
  {
   "fact": "density",
   "text": "Look for the rows to stay blank white, since 0% of the cells ever turn black here."
  },
  {
   "fact": "centre_column",
   "text": "Follow the centre column down the quilt - just 1 of the first 32 cells is black, the rest white."
  },
  {
   "fact": "spread",
   "text": "Watch the edges hold still - the pattern spreads 0 cells per row on both the left and right."
  }
 ]
}

The name ADA under rule 110, wishing for travelling shapes - already there

Input:

{
 "task": "reading",
 "rule": "110",
 "seed": "the name ADA spelled as 11 cells: 10010000001",
 "wish": "Something alive - little shapes that travel and bump into each other",
 "page_facts": "rule | 110 - laws BBB->W BBW->B BWB->B BWW->W WBB->B WBW->B WWB->B WWW->W\nseed | the name ADA spelled as 11 cells: 10010000001\nestimate | edge - a known complex rule - steady stretches with structures that travel and collide; the page's estimate over 64 rows\ndensity | 33% of the cells inside the growing pattern are black\nspread | the left side moves 1 cell per row, the right side 0 cells per row\nwidest_gap | 8 white cells with black on both sides, at row 39\ncentre_column | rows 0-31 down the middle: 00000110111100000101010101011000 (13 black of 32)\ntwins | mirror twin rule 124; colour-swapped twin rule 137\necho | proved able, in principle, to carry out any computation given enough room and time\nflip BBB | W becomes B -> rule 238: repeats, density 57%\nflip BBW | B becomes W -> rule 46: repeats, density 8%\nflip BWB | B becomes W -> rule 78: repeats, density 32%\nflip BWW | W becomes B -> rule 126: churns, density 46%\nflip WBB | B becomes W -> rule 102: churns, density 29%\nflip WBW | B becomes W -> rule 106: repeats, density 4%\nflip WWB | B becomes W -> rule 108: repeats, density 4%\nflip WWW | W becomes B -> rule 111: repeats, density 53%\nverdict_rule | already when the current estimate equals wish_target; else one_flip when at least one flip's estimate equals wish_target; else no_flip"
}

Reply:

{
 "task": "reading",
 "title": "Travelers of the Calm Edge",
 "wish_target": "edge",
 "verdict": "already",
 "verdict_reason": "The page's estimate already shows this universe lives on the edge, with steady stretches and colliding, traveling structures, which is exactly the lively, bumping shapes the wish asks for.",
 "pick_flip": "",
 "flip_note": "The estimate itself is the tell: this rule already lives on the edge, its steady stretches broken by structures that travel and collide.",
 "philosophy": "From the name ADA's eleven seed cells, rule 110 grows unevenly: its left edge advances one cell every row while its right edge holds still. Black cells settle near 33% of the grid, calm for long stretches, until a gap of eight white cells opens and small structures travel and collide, true to a universe that lives on the edge. The pattern is proved able, in principle, to carry out any computation given enough room and time.",
 "look_for": [
  {
   "fact": "widest_gap",
   "text": "Find the stretch near row 39 where eight white cells sit between black walls - the widest calm gap in the quilt."
  },
  {
   "fact": "density",
   "text": "About 33% of the cells are black, speckled through the pattern rather than packed into solid blocks."
  },
  {
   "fact": "spread",
   "text": "Watch how the left edge creeps out one cell every row while the right edge never moves at all."
  }
 ]
}

The name JUNIPER under rule 184, wishing it would fade - one flip away

Input:

{
 "task": "reading",
 "rule": "184",
 "seed": "the name JUNIPER spelled as 33 cells: 101010101011100100110000001011001",
 "wish": "Let it all fade away to nothing",
 "page_facts": "rule | 184 - laws BBB->B BBW->W BWB->B BWW->B WBB->B WBW->W WWB->W WWW->W\nseed | the name JUNIPER spelled as 33 cells: 101010101011100100110000001011001\nestimate | repeats - it falls into a beat - the same rows again, standing still or sliding sideways (every 1 row, sliding 1 cell right each time); the page's estimate over 64 rows\ndensity | 15% of the cells inside the growing pattern are black\nspread | the left side moves 0 cells per row, the right side 1 cell per row\nwidest_gap | 6 white cells with black on both sides, at row 1\ncentre_column | rows 0-31 down the middle: 01001010101010101010000000000000 (9 black of 32)\ntwins | mirror twin rule 226; colour-swapped twin rule 226\necho | a classic toy model of traffic: black cells are cars that move right when the space ahead is free\nflip BBB | B becomes W -> rule 56: repeats, density 14%\nflip BBW | W becomes B -> rule 248: repeats, density 59%\nflip BWB | B becomes W -> rule 152: repeats, density 5%\nflip BWW | B becomes W -> rule 168: fades, density 1%\nflip WBB | B becomes W -> rule 176: repeats, density 12%\nflip WBW | W becomes B -> rule 188: repeats, density 51%\nflip WWB | W becomes B -> rule 186: repeats, density 55%\nflip WWW | W becomes B -> rule 185: repeats, density 84%\nverdict_rule | already when the current estimate equals wish_target; else one_flip when at least one flip's estimate equals wish_target; else no_flip"
}

Reply:

{
 "task": "reading",
 "title": "Juniper's Rightward Sliding Beat",
 "wish_target": "fades",
 "verdict": "one_flip",
 "verdict_reason": "The current pattern repeats in a sliding beat, but flipping law BWW gives rule 168, which fades to nothing.",
 "pick_flip": "BWW",
 "flip_note": "Flipping law BWW turns this into rule 168, which fades away, settling down to just 1% density.",
 "philosophy": "Starting from the name JUNIPER as thirty-three cells, rule 184 settles almost at once into a steady beat: the same row returns, creeping one cell right each time while the left edge holds still. About fifteen percent of the cells stay black, with one wide gap of six empty cells near the start. It echoes a classic model of traffic, where black cells are cars that move right when the space ahead is free.",
 "look_for": [
  {
   "fact": "spread",
   "text": "Watch the left edge hold at 0 cells of drift while the right edge slides 1 cell over on every row."
  },
  {
   "fact": "density",
   "text": "Only about 15% of the cells in the pattern are black - scan each row for how thin the black islands stay."
  },
  {
   "fact": "widest_gap",
   "text": "Near the top, find the widest stretch: 6 white cells in a row with black cells fencing both ends."
  }
 ]
}

Build the input yourself

Load the script in node (global.window = global; require("./cellkit.js")), then:

const K = CellKit;
const built = K.buildInput({ rule: 30, seed: "one", name: "", wish: "Triangles inside triangles, like old lace" });
built.d;                                         // { rule, cls, density, gap, spread, centre, flips: [{ law, rule, cls, density }], ... } - the free universe
const wire = K.toWire(built.input);              // POST this to /estimate and /run
// then: const reply = K.normalize(K.parseReply(job.output.output));
// K.check(reply, built).notes                   // the page's re-check of the reading
// K.verdictFor(built.d, reply.wish_target)      // the verdict rule for the reply's own wish target

Step 1 — a tiny client helper

One function that sends JSON and unwraps the envelope. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="paste-your-token-here"   # from https://one-black-cell.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"one-black-cell"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.

# input.json: the run input object - {"task": "reading", "rule": "30", "seed": "one black cell", "wish": "...", "page_facts": "..."} - see the field table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses one-black-cell:<task>:<inputHash>:a<attempt> (the one task is reading), and its one reformat retry (with retry_note added) is attempt 2, a new key and a second run. The job's output.output is the reply text; truncated: true means the balance cut it short.

KEY="one-black-cell:reading:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}' 

Step 6 — POST /run-stream

The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: one-black-cell:reading:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json