Initial public ModelForge release
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# First run
|
||||
|
||||
You have a healthy control plane. Nothing is serving yet, and that is correct — this page is how you
|
||||
get from there to a capability an application can call.
|
||||
|
||||
## What you have after installation
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| Projects | seeded from the shipped manifests |
|
||||
| Capability contracts | **none** |
|
||||
| Capability deployments | **none** |
|
||||
| Model candidates | seeded as a discovery list, none approved |
|
||||
| Policies | lifecycle, migration, observability and recovery defaults present |
|
||||
| Compute nodes | none until you enrol one |
|
||||
|
||||
Contracts and deployments are absent on purpose. A contract is lifecycle-owned, and a deployment
|
||||
requires a model artifact that has been acquired, verified and promoted. ModelForge will not
|
||||
fabricate either from a manifest, so the bindings that are waiting for a contract are *reported*
|
||||
rather than invented:
|
||||
|
||||
```text
|
||||
SYNCED project registry 8 binding(s) waiting for a contract
|
||||
```
|
||||
|
||||
## 1. Enrol a compute node
|
||||
|
||||
Nothing can serve without a node. See [NODE_AGENT.md](NODE_AGENT.md) for the full procedure. In
|
||||
short:
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "X-ModelForge-Admin-Token: $KEY" -H "Content-Type: application/json" \
|
||||
-d '{"display_name":"GPU Node","expires_in_seconds":900}' \
|
||||
http://127.0.0.1:8000/api/v1/admin/node-enrollments
|
||||
```
|
||||
|
||||
The token it returns is **single use**. A concurrent storm against one token produces exactly one
|
||||
identity and one credential — that is enforced by an atomic claim, not by timing.
|
||||
|
||||
Give the token to the agent on the GPU host and start it. The node appears with liveness `online`.
|
||||
|
||||
## 2. Discover a model candidate
|
||||
|
||||
The Discover workspace lists the candidates in `config/models/initial-candidates.yaml`. Nothing in
|
||||
that file is approved by being listed — it is a starting point for verification.
|
||||
|
||||
## 3. Acquire and verify the artifact
|
||||
|
||||
Acquisition downloads into quarantine, records a per-file SHA-256, and runs the supply-chain checks
|
||||
before anything is promoted to the verified artifact root. `trust_remote_code` is never enabled;
|
||||
production refuses to start if it is.
|
||||
|
||||
An artifact that fails verification stays in quarantine. That is the system working.
|
||||
|
||||
## 4. Register a capability contract and promote a deployment
|
||||
|
||||
Capabilities are the interface applications bind to. A contract fixes the input and output schema
|
||||
for a version; a deployment is a specific artifact set and runtime profile serving that contract.
|
||||
|
||||
Promotion runs through the lifecycle: plan, approval, execution, and an immutable snapshot that lets
|
||||
an interrupted operation roll back exactly once. `no_hidden_auto_promotion` asserts that no
|
||||
production deployment exists without a recorded production approval — there is no path that quietly
|
||||
promotes something.
|
||||
|
||||
See [MODEL_LIFECYCLE.md](product/MODEL_LIFECYCLE.md).
|
||||
|
||||
## 5. Create a service client for the application
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "X-ModelForge-Admin-Token: $KEY" -H "Content-Type: application/json" \
|
||||
-d '{"name":"examplerag-production","allowed_capabilities":["rag.embedding@1"],
|
||||
"project_key":"examplerag","integration_environment":"production",
|
||||
"purpose":"ExampleRAG production embedding"}' \
|
||||
http://127.0.0.1:8000/api/v1/admin/service-clients
|
||||
```
|
||||
|
||||
The response carries the credential **once**. Only a SHA-256 hash is stored, with a bounded prefix
|
||||
for identification. There is no way to read it back — rotate if you lose it.
|
||||
|
||||
## 6. Call the capability
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: Bearer $CREDENTIAL" -H "Content-Type: application/json" \
|
||||
-d '{"input":["the text to embed"]}' \
|
||||
http://127.0.0.1:8000/api/v1/capabilities/rag.embedding@1/invoke
|
||||
```
|
||||
|
||||
The application names a **capability**, never a model, a node or a file path. That is the whole point
|
||||
of the platform: you can replace what is behind `rag.embedding@1` without the caller knowing, and the
|
||||
caller cannot accidentally depend on which model answered.
|
||||
|
||||
See [PROJECT_INTEGRATION.md](PROJECT_INTEGRATION.md).
|
||||
|
||||
## What you should see when it is working
|
||||
|
||||
| Signal | Healthy value |
|
||||
| --- | --- |
|
||||
| `/api/v1/health/ready` | 200 |
|
||||
| Node liveness | `online` |
|
||||
| `/api/v1/capabilities` | your contract listed |
|
||||
| Operations overview | no firing alerts |
|
||||
| Recovery dashboard | a recent verified backup, no unprotected assets |
|
||||
|
||||
## Common first-run outcomes
|
||||
|
||||
| What you see | What it means |
|
||||
| --- | --- |
|
||||
| `503 NO_ELIGIBLE_NODE` | No online node can serve this capability. Check node liveness first. |
|
||||
| `CAPACITY_CONSTRAINED` | A node exists but has no headroom. This is a correct refusal, not an error. |
|
||||
| `401 CAPABILITY_NOT_AUTHORIZED` | The credential is missing, revoked, expired, or not scoped to this capability. |
|
||||
| `8 binding(s) waiting for a contract` | Expected before you have promoted anything. |
|
||||
|
||||
More in [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||
Reference in New Issue
Block a user