{"openapi":"3.1.0","info":{"title":"AIRigs Recipes API","version":"1.0.0","description":"Reproducible local-AI recipes pinned to benchmarked AIRigs rigs. Authentication is an AIRigs OAuth2 access token sent as a Bearer token.","license":{"name":"MIT"}},"servers":[{"url":"https://recipes.airigs.net/api/v1"}],"x-airigs-engines":["vllm","sglang","llamacpp","strata","omlx","ollama","custom"],"x-airigs-rig-id-pattern":"^r_[A-Za-z0-9_-]{1,92}$","x-airigs-api-version":"v1","tags":[{"name":"meta"},{"name":"recipes"},{"name":"rigs"},{"name":"catalog"},{"name":"identity"}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"Authorization code + PKCE against https://airigs.net. Confidential client airigs-recipes.","flows":{"authorizationCode":{"authorizationUrl":"https://airigs.net/oauth/authorize","tokenUrl":"https://airigs.net/oauth/token","refreshUrl":"https://airigs.net/oauth/token","scopes":{"user:read":"Read the signed-in user","rig:read":"Read the user’s rigs","rig:write":"Write the user’s rigs","recipe:read":"Read recipes","recipe:write":"Create and update recipes"}}}}},"schemas":{"Recipe":{"type":"object","required":["slug","title","summary","author","engine","models","args","status","rigId"],"properties":{"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","maxLength":80},"title":{"type":"string","maxLength":120},"summary":{"type":"string","maxLength":300},"author":{"type":"object","required":["uid","username","userslug"],"properties":{"uid":{"oneOf":[{"type":"integer"},{"type":"string"}]},"username":{"type":"string"},"userslug":{"type":"string"}}},"engine":{"type":"string","enum":["vllm","sglang","llamacpp","strata","omlx","ollama","custom"]},"models":{"type":"array","maxItems":8,"items":{"$ref":"#/components/schemas/Model"}},"args":{"type":"array","maxItems":40,"items":{"$ref":"#/components/schemas/Arg"}},"launchCommand":{"type":"string","maxLength":4000},"customInstructions":{"type":"string","maxLength":8000},"repos":{"type":"array","maxItems":10,"items":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"note":{"type":"string","maxLength":200}}}},"hardwareNotes":{"type":"string","maxLength":4000},"status":{"type":"string","enum":["draft","published"]},"version":{"type":"integer","description":"Monotonic write version used for conditional lifecycle updates."},"rigId":{"type":"string","pattern":"^r_[A-Za-z0-9_-]{1,92}$"},"rigRevision":{"type":["integer","null"],"description":"Rig revision pinned at publish time."},"rigRemoteRevision":{"type":["integer","null"],"description":"Latest revision seen on AIRigs, if it differs."},"rigStale":{"type":"boolean","description":"The rig changed on AIRigs; the page keeps the published snapshot."},"rigMissing":{"type":"boolean","description":"The rig could not be found on AIRigs; the snapshot is retained."},"rigSnapshot":{"type":"object","additionalProperties":true,"description":"Immutable snapshot of the AIRigs rig at publish time."},"verification":{"type":["object","null"],"description":"AIRigs verification metadata carried from the rig snapshot. plausibilityChecked:false means the catalogue did not resolve the GPU; the benchmark is still self-reported.","properties":{"method":{"type":"string"},"plausibilityChecked":{"type":"boolean"},"reason":{"type":"string"}}},"rigSnapshotAt":{"type":"string","format":"date-time"},"benchmark":{"$ref":"#/components/schemas/RecipeBenchmark"},"benchmarkRigRevision":{"type":["integer","null"],"description":"Rig revision the attached benchmark was measured on (server-stamped)."},"benchmarkRevisionState":{"type":"string","enum":["none","unbound","mismatch","ok"],"description":"Whether the benchmark is bound to the pinned rig revision."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"publishedAt":{"type":"string","format":"date-time","nullable":true},"url":{"type":"string"},"scriptUrl":{"type":"string"},"rig":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"verified":{"type":"boolean"},"verifiedAt":{"type":"string","format":"date-time","nullable":true},"verification":{"type":["object","null"],"properties":{"method":{"type":"string"},"plausibilityChecked":{"type":"boolean"},"reason":{"type":"string"}}}}}}},"RecipeInput":{"type":"object","required":["title","summary","engine","models","rigId"],"properties":{"title":{"type":"string","minLength":3,"maxLength":120},"summary":{"type":"string","minLength":10,"maxLength":300},"engine":{"type":"string","enum":["vllm","sglang","llamacpp","strata","omlx","ollama","custom"]},"models":{"type":"array","minItems":1,"maxItems":8,"items":{"$ref":"#/components/schemas/Model"}},"args":{"type":"array","maxItems":40,"items":{"$ref":"#/components/schemas/Arg"}},"launchCommand":{"type":"string","maxLength":4000},"customInstructions":{"type":"string","maxLength":8000},"repos":{"type":"array","maxItems":10,"items":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"note":{"type":"string","maxLength":200}}}},"hardwareNotes":{"type":"string","maxLength":4000},"rigId":{"type":"string","pattern":"^r_[A-Za-z0-9_-]{1,92}$"},"publish":{"type":"boolean","description":"Run the publish checks after creating the draft."}}},"Model":{"type":"object","required":["source","id","name","role"],"properties":{"source":{"type":"string","enum":["huggingface","modelscope","custom"]},"id":{"type":"string","maxLength":200},"name":{"type":"string","maxLength":160},"role":{"type":"string","enum":["base","drafter","adapter","vision","embedding"]},"url":{"type":"string","format":"uri"}}},"Arg":{"type":"object","required":["flag"],"properties":{"flag":{"type":"string","maxLength":64,"example":"--max-model-len"},"value":{"type":"string","maxLength":400,"example":"32768"}}},"RecipeBenchmark":{"type":"object","required":["schemaVersion","rigId","engine","startedAt","finishedAt","modelIds","concurrency","ttftMs","outputTokensPerSec","vramPeakBytes","powerAvgW"],"properties":{"schemaVersion":{"type":"integer","enum":[1]},"rigId":{"type":"string"},"rigRevision":{"type":"integer","description":"Rig revision at measurement time. Stamped by the server from the author’s verified rig when the benchmark is attached; client-supplied values are ignored."},"engine":{"type":"string","enum":["vllm","sglang","llamacpp","strata","omlx","ollama","custom"]},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":"string","format":"date-time"},"modelIds":{"type":"array","minItems":1,"maxItems":16,"items":{"type":"string"}},"contextTokens":{"type":"integer","minimum":1,"maximum":1000000000},"concurrency":{"type":"integer","minimum":1,"maximum":4096},"requests":{"type":"integer","minimum":1,"maximum":10000000},"ttftMs":{"type":"number","minimum":0.1,"maximum":600000},"promptTokensPerSec":{"type":"number","minimum":0.01,"maximum":1000000},"outputTokensPerSec":{"type":"number","minimum":0.01,"maximum":1000000},"totalTokensPerSec":{"type":"number","minimum":0.01,"maximum":1000000},"vramPeakBytes":{"type":"integer","minimum":1,"maximum":1099511627776},"ramPeakBytes":{"type":"integer","minimum":0,"maximum":1099511627776},"powerAvgW":{"type":"number","minimum":0,"maximum":5000},"gpuUtilPct":{"type":"number","minimum":0,"maximum":100},"notes":{"type":"string","maxLength":1000}}},"Error":{"type":"object","required":["status"],"properties":{"status":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}}},"errors":{"type":"array","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/health":{"get":{"tags":["meta"],"summary":"Liveness and AIRigs reachability","responses":{"200":{"description":"Service status","content":{"application/json":{"schema":{"type":"object","required":["ok","version","airigs"],"properties":{"ok":{"type":"boolean"},"version":{"type":"string"},"airigs":{"type":"object","properties":{"reachable":{"type":"boolean"}}},"request":{"type":"object","properties":{"ip":{"type":"string"},"trustProxy":{"oneOf":[{"type":"boolean"},{"type":"string"}],"description":"Resolved Express trust proxy setting; false means socket addresses."}}}}}}}}}}},"/me":{"get":{"tags":["identity"],"summary":"The signed-in user, their rigs, and whether they can publish","security":[{"oauth2":["user:read"]}],"responses":{"200":{"description":"Caller identity","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","additionalProperties":true},"rigs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"verified":{"type":"boolean"}}}},"canPublish":{"type":"boolean"}}}}}},"401":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipes":{"get":{"tags":["recipes"],"summary":"List published recipes","parameters":[{"name":"q","in":"query","schema":{"type":"string","maxLength":80}},{"name":"engine","in":"query","schema":{"type":"string"}},{"name":"model","in":"query","schema":{"type":"string","maxLength":80}},{"name":"author","in":"query","schema":{"type":"string","maxLength":120}},{"name":"sort","in":"query","schema":{"type":"string","enum":["recent","fastest"],"default":"recent"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"A page of published recipes","content":{"application/json":{"schema":{"type":"object","properties":{"recipes":{"type":"array","items":{"$ref":"#/components/schemas/Recipe"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"}}}}}}}},"post":{"tags":["recipes"],"summary":"Create a draft (optionally publish immediately)","security":[{"oauth2":["recipe:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"401":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipes/{slug}":{"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["recipes"],"summary":"Get a published recipe, or a draft if you are its author","responses":{"200":{"description":"Recipe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["recipes"],"summary":"Update a recipe you own","security":[{"oauth2":["recipe:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipeInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["recipes"],"summary":"Delete a recipe you own","security":[{"oauth2":["recipe:write"]}],"responses":{"204":{"description":"Deleted"},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipes/{slug}/benchmark":{"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"delete":{"tags":["recipes"],"summary":"Clear a draft benchmark","description":"Author-only. Published recipes keep their benchmark; replace it instead. Returns the recipe with benchmark:null.","security":[{"oauth2":["recipe:write"]}],"responses":{"200":{"description":"Benchmark cleared","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["recipes"],"summary":"Attach or replace the recipe benchmark (optionally publish)","description":"Requires recipe:write and rig:read. The server fetches the author’s current verified rig and stamps benchmark.rigRevision; publishing later rejects a benchmark whose revision no longer matches.","security":[{"oauth2":["recipe:write","rig:read"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["benchmark"],"properties":{"benchmark":{"$ref":"#/components/schemas/RecipeBenchmark"},"benchmarkRigRevision":{"type":["integer","null"],"description":"Rig revision the attached benchmark was measured on (server-stamped)."},"benchmarkRevisionState":{"type":"string","enum":["none","unbound","mismatch","ok"],"description":"Whether the benchmark is bound to the pinned rig revision."},"publish":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Benchmark stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipes/{slug}/publish":{"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["recipes"],"summary":"Publish a draft","description":"Requires a verified rig on the author’s account, at least one model, launch instructions, and a benchmark measured on the same rig. The rig is snapshotted onto the recipe.","security":[{"oauth2":["recipe:write"]}],"responses":{"200":{"description":"Published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recipe"}}}},"403":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipes/{slug}/script":{"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"format","in":"query","schema":{"type":"string","enum":["sh","raw"],"default":"sh"}}],"get":{"tags":["recipes"],"summary":"Download the generated POSIX shell script","description":"Available to everyone for published recipes and to the author for drafts. Cache-Control: no-store.","responses":{"200":{"description":"Shell script","headers":{"Content-Disposition":{"schema":{"type":"string"}},"Cache-Control":{"schema":{"type":"string","enum":["no-store"]}}},"content":{"text/x-shellscript":{"schema":{"type":"string"}}}},"404":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/models/search":{"get":{"tags":["catalog"],"summary":"Search models through the AIRigs catalogue","description":"Requires an active AIRigs bearer token or a signed-in browser session. The proxy is rate-limited per client and by a global upstream budget, and oversized upstream responses are refused.","security":[{"oauth2":[]}],"parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"source","in":"query","schema":{"type":"string","enum":["huggingface","modelscope"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":20,"default":10}}],"responses":{"200":{"description":"Catalogue results (proxied and cached for 60s)"},"400":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rigs":{"get":{"tags":["rigs"],"summary":"The caller’s AIRigs rigs","security":[{"oauth2":["rig:read"]}],"responses":{"200":{"description":"Rigs for this token"},"401":{"description":"JSON error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.json":{"get":{"tags":["meta"],"summary":"This document","responses":{"200":{"description":"OpenAPI 3.1 document"}}}}}}