{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://staging.localfirst.sh/schemas/project/v0.2.json","title":"Localfirst portable project specification (localfirst/project/v0.2)","type":"object","properties":{"schema":{"type":"string","const":"localfirst/project/v0.2"},"project":{"type":"object","properties":{"key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":2,"maxLength":120},"summary":{"type":"string","minLength":10,"maxLength":400},"type":{"type":"string","enum":["application","workflow","demo","robotics","experiment","hardware_build","documentation"]},"categories":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"string","enum":["text-classification","text-generation","chat-assistant","summarization","translation","embeddings-retrieval","code-assistant","agent-workflow","speech-to-text","text-to-speech","image-generation","vision-language","object-detection","robotics-control","model-serving","benchmarking","hardware-setup","documentation"]}},"version":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9A-Za-z][0-9A-Za-z.\\-+]*$"},"license":{"type":"object","properties":{"state":{"type":"string","enum":["known","unknown","not_applicable"]},"id":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"reference":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["state"],"additionalProperties":false}},"required":["key","name","summary","type","categories","version","license"],"additionalProperties":false},"sources":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"kind":{"type":"string","enum":["repository","documentation","hardware_note","media","authored_specification"]},"url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]},"revision":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]},"content_digest":{"default":null,"anyOf":[{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"},{"type":"null"}]},"path":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._\\-/]*$"},{"type":"null"}]},"license":{"default":null,"anyOf":[{"type":"object","properties":{"state":{"type":"string","enum":["known","unknown","not_applicable"]},"id":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"reference":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["state"],"additionalProperties":false},{"type":"null"}]},"note":{"default":null,"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}},"required":["key","kind","url"],"additionalProperties":false}},"attribution":{"type":"object","properties":{"authors":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"url":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["name"],"additionalProperties":false}},"relationship":{"type":"string","enum":["original_author_claimed","maintainer_claimed","curator_submitted","third_party_submitted","unknown"]}},"required":["authors","relationship"],"additionalProperties":false},"models":{"type":"object","properties":{"state":{"type":"string","enum":["declared","unknown","not_applicable"]},"roles":{"default":[],"maxItems":8,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"required_capabilities":{"minItems":1,"maxItems":9,"type":"array","items":{"type":"string","enum":["chat","completion","json_output","tool_calls","vision","audio_input","audio_output","embeddings","reasoning"]}},"minimum_context_tokens":{"default":null,"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":100000000},{"type":"null"}]},"substitution":{"type":"string","enum":["allowed_for_task_test","not_allowed","unknown"]},"preferred":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]}},"required":["key","required_capabilities","substitution"],"additionalProperties":false}}},"required":["state"],"additionalProperties":false},"runtimes":{"type":"object","properties":{"state":{"type":"string","enum":["declared","unknown","not_applicable"]},"supported":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"family":{"type":"string","enum":["ollama","lmstudio","llamacpp","vllm","mlx","tensorrt_llm","onnxruntime","transformers","other"]},"version_constraint":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]}},"required":["family"],"additionalProperties":false}}},"required":["state"],"additionalProperties":false},"hardware":{"type":"object","properties":{"state":{"type":"string","enum":["declared","unknown","not_applicable"]},"architectures":{"default":[],"maxItems":3,"type":"array","items":{"type":"string","enum":["arm64","x86_64","riscv64"]}},"minimum_memory_bytes":{"default":null,"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"minimum_vram_bytes":{"default":null,"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"accelerator":{"default":"unknown","type":"string","enum":["required","optional","none","unknown"]},"devices":{"default":[],"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":120}}},"required":["state"],"additionalProperties":false},"operating_systems":{"type":"object","properties":{"state":{"type":"string","enum":["declared","unknown","not_applicable"]},"supported":{"default":[],"maxItems":4,"type":"array","items":{"type":"string","enum":["macos","linux","windows","other"]}}},"required":["state"],"additionalProperties":false},"dependencies":{"type":"object","properties":{"state":{"type":"string","enum":["declared","unknown","not_applicable"]},"items":{"default":[],"maxItems":50,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"kind":{"type":"string","enum":["system","language_runtime","package","container","other"]},"version_constraint":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"license":{"default":null,"anyOf":[{"type":"object","properties":{"state":{"type":"string","enum":["known","unknown","not_applicable"]},"id":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"reference":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["state"],"additionalProperties":false},{"type":"null"}]}},"required":["name","kind"],"additionalProperties":false}}},"required":["state"],"additionalProperties":false},"configurations":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"model_roles":{"default":[],"maxItems":8,"type":"array","items":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"setup_kind":{"type":"string","enum":["manual","script_documented","container_documented","unsupported"]},"workload":{"default":{"context_tokens":null,"concurrency":null},"type":"object","properties":{"context_tokens":{"default":null,"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":100000000},{"type":"null"}]},"concurrency":{"default":null,"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1024},{"type":"null"}]}},"additionalProperties":false},"note":{"default":null,"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}},"required":["key","setup_kind"],"additionalProperties":false}},"data_boundary":{"type":"object","properties":{"inference":{"type":"string","enum":["local_required","local_preferred","external_allowed","unknown"]},"storage":{"type":"string","enum":["local","external","unknown"]},"runtime_network":{"type":"string","enum":["required","optional","none","unknown"]},"external_services":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}}},"required":["inference","storage","runtime_network"],"additionalProperties":false},"test_support":{"type":"string","enum":["none","manual","declarative_task_v1"]},"tests":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"schema":{"type":"string","const":"localfirst/test/v0.1"},"path":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._\\-/]*$"}},"required":["key","schema","path"],"additionalProperties":false}},"limitations":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":5,"maxLength":500}},"evidence":{"default":[],"maxItems":20,"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["documented","claimed"]},"summary":{"type":"string","minLength":1,"maxLength":500},"reference":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["kind","summary"],"additionalProperties":false}},"media":{"default":[],"maxItems":10,"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video","diagram","other"]},"url":{"type":"string","maxLength":2048},"caption":{"default":null,"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"license":{"default":null,"anyOf":[{"type":"object","properties":{"state":{"type":"string","enum":["known","unknown","not_applicable"]},"id":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"reference":{"default":null,"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]}},"required":["state"],"additionalProperties":false},{"type":"null"}]}},"required":["kind","url"],"additionalProperties":false}},"derivative_of":{"default":null,"anyOf":[{"type":"object","properties":{"project_key":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"project_id":{"default":null,"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"project_version_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"version":{"default":null,"anyOf":[{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9A-Za-z][0-9A-Za-z.\\-+]*$"},{"type":"null"}]},"change_summary":{"type":"string","minLength":5,"maxLength":1000}},"required":["project_key","change_summary"],"additionalProperties":false},{"type":"null"}]},"extensions":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{}}},"required":["schema","project","sources","attribution","models","runtimes","hardware","operating_systems","dependencies","configurations","data_boundary","test_support","limitations"],"additionalProperties":false}