# Contrato de unidad v1: JSON Schemas

Estos son los esquemas del [contrato v1](/es/units-code/contract), borrador 2020-12, copiados tal cual de los archivos que incorpora el agente y contra los que valida. Cada uno también se sirve como archivo en `/contract/units/v1/`. Las URL de `$id` son los identificadores canónicos.

### `POST /accepting request body`

A JSON boolean (the literal true or false). An empty body means true. The signals ready, idle, busy and shutdown take no body. The same text is the content of the file signals/accepting.

[Archivo original: `accepting-request.schema.json`](/contract/units/v1/accepting-request.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/accepting-request.schema.json",
  "title": "POST /accepting request body",
  "description": "A JSON boolean (the literal true or false). An empty body means true. The signals ready, idle, busy and shutdown take no body. The same text is the content of the file signals/accepting.",
  "type": "boolean"
}
```

### `/zaroz/allocation.json`

null while the unit is not allocated. When allocated, user-data.json already holds the allocation's user_data.

[Archivo original: `allocation.schema.json`](/contract/units/v1/allocation.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/allocation.schema.json",
  "title": "/zaroz/allocation.json",
  "description": "null while the unit is not allocated. When allocated, user-data.json already holds the allocation's user_data.",
  "oneOf": [
    {
      "type": "null"
    },
    {
      "type": "object",
      "required": [
        "id",
        "allocated_at",
        "generation"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "allocated_at": {
          "type": "string",
          "format": "date-time"
        },
        "generation": {
          "type": "integer"
        }
      }
    }
  ]
}
```

### `/zaroz/contract.json`

Which contract the files in this directory follow. Read it first.

[Archivo original: `contract.schema.json`](/contract/units/v1/contract.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/contract.schema.json",
  "title": "/zaroz/contract.json",
  "description": "Which contract the files in this directory follow. Read it first.",
  "type": "object",
  "required": [
    "version",
    "agent_version"
  ],
  "properties": {
    "version": {
      "const": 1,
      "description": "Contract major version. Within a major, changes are additive only."
    },
    "agent_version": {
      "type": "string",
      "minLength": 1
    }
  }
}
```

### `Error response of the signal socket`



[Archivo original: `error.schema.json`](/contract/units/v1/error.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/error.schema.json",
  "title": "Error response of the signal socket",
  "type": "object",
  "required": [
    "error"
  ],
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "string",
          "description": "Stable machine-readable code, for example unknown_signal, invalid_body, invalid_wait, not_ready, body_too_large."
        },
        "message": {
          "type": "string"
        }
      }
    }
  }
}
```

### `Unit event envelope`

One event of the units event log, as returned by the events route and delivered to webhooks. Consumers must ignore unknown fields and unknown event types.

[Archivo original: `event.schema.json`](/contract/units/v1/event.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/event.schema.json",
  "title": "Unit event envelope",
  "description": "One event of the units event log, as returned by the events route and delivered to webhooks. Consumers must ignore unknown fields and unknown event types.",
  "type": "object",
  "required": [
    "seq",
    "id",
    "type",
    "contract_version",
    "occurred_at",
    "service_instance_id",
    "unit",
    "data"
  ],
  "properties": {
    "seq": {
      "type": "integer",
      "minimum": 0,
      "description": "Gapless and strictly increasing per Workload, from 1. Only the delivery-only webhook.ping may carry 0 (the cursor of a webhook that has delivered nothing yet)."
    },
    "id": {
      "type": "string",
      "pattern": "^evt_[0-9a-fA-F-]{32,36}$"
    },
    "type": {
      "type": "string",
      "minLength": 1
    },
    "contract_version": {
      "const": 1
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "service_instance_id": {
      "type": "string",
      "minLength": 1
    },
    "unit": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "required": [
            "id",
            "key",
            "labels"
          ],
          "properties": {
            "id": {
              "type": "string",
              "minLength": 1
            },
            "key": {
              "type": "string",
              "minLength": 1,
              "maxLength": 40
            },
            "labels": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        }
      ]
    },
    "data": {
      "type": "object"
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "type": {
            "const": "webhook.ping"
          }
        },
        "required": [
          "type"
        ]
      },
      "else": {
        "properties": {
          "seq": {
            "minimum": 1
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "const": "unit.exited"
          }
        },
        "required": [
          "type"
        ]
      },
      "then": {
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "code",
              "restarted"
            ],
            "properties": {
              "code": {
                "type": "integer"
              },
              "restarted": {
                "type": "boolean"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "unit.failed",
              "unit.destroyed"
            ]
          }
        },
        "required": [
          "type"
        ]
      },
      "then": {
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "reason"
            ],
            "properties": {
              "reason": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "const": "units.scaled"
          }
        },
        "required": [
          "type"
        ]
      },
      "then": {
        "properties": {
          "unit": {
            "type": "null"
          },
          "data": {
            "type": "object",
            "required": [
              "from",
              "to",
              "reason"
            ],
            "properties": {
              "from": {
                "type": "integer",
                "minimum": 0
              },
              "to": {
                "type": "integer",
                "minimum": 0
              },
              "reason": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "unit.ready",
              "unit.allocated"
            ]
          }
        },
        "required": [
          "type"
        ]
      },
      "then": {
        "properties": {
          "unit": {
            "type": "object"
          },
          "data": {
            "type": "object",
            "required": [
              "private_name"
            ],
            "properties": {
              "private_name": {
                "type": "string"
              },
              "public_endpoints": {
                "type": "array"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "enum": [
              "unit.created",
              "unit.starting",
              "unit.accepting",
              "unit.not_accepting",
              "unit.allocation_delivered",
              "unit.allocation_undelivered",
              "unit.idle",
              "unit.busy",
              "unit.draining",
              "unit.stopped"
            ]
          }
        },
        "required": [
          "type"
        ]
      },
      "then": {
        "properties": {
          "unit": {
            "type": "object"
          }
        }
      }
    }
  ]
}
```

### `Response of POST /<signal>`



[Archivo original: `signal-response.schema.json`](/contract/units/v1/signal-response.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/signal-response.schema.json",
  "title": "Response of POST /<signal>",
  "type": "object",
  "required": [
    "ok",
    "signal",
    "changed"
  ],
  "properties": {
    "ok": {
      "const": true
    },
    "signal": {
      "enum": [
        "ready",
        "accepting",
        "idle",
        "busy",
        "shutdown"
      ]
    },
    "changed": {
      "type": "boolean",
      "description": "false when the signal repeated the current state and was coalesced."
    }
  }
}
```

### `/zaroz/state.json`

Whether the unit is being drained. A draining unit has already left the load-balanced set; finish up and exit before the pod's termination grace ends.

[Archivo original: `state.schema.json`](/contract/units/v1/state.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/state.schema.json",
  "title": "/zaroz/state.json",
  "description": "Whether the unit is being drained. A draining unit has already left the load-balanced set; finish up and exit before the pod's termination grace ends.",
  "oneOf": [
    {
      "type": "object",
      "required": [
        "draining"
      ],
      "properties": {
        "draining": {
          "const": false
        }
      }
    },
    {
      "type": "object",
      "required": [
        "draining",
        "reason"
      ],
      "properties": {
        "draining": {
          "const": true
        },
        "reason": {
          "enum": [
            "stop",
            "destroy",
            "replace",
            "scale_down"
          ]
        }
      }
    }
  ]
}
```

### `UnitSignal (units.zaroz.cloud/v1) spec`

Written by the kernel, read by the agent. The agent ignores unknown fields and refuses a spec that fails validation, keeping the previous contract.

[Archivo original: `unit-signal-spec.schema.json`](/contract/units/v1/unit-signal-spec.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/unit-signal-spec.schema.json",
  "title": "UnitSignal (units.zaroz.cloud/v1) spec",
  "description": "Written by the kernel, read by the agent. The agent ignores unknown fields and refuses a spec that fails validation, keeping the previous contract.",
  "type": "object",
  "required": [
    "unitId",
    "key",
    "contractVersion",
    "generation"
  ],
  "properties": {
    "unitId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "key": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40,
      "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
    },
    "labels": {
      "type": "object",
      "maxProperties": 16,
      "propertyNames": {
        "minLength": 1,
        "maxLength": 32,
        "pattern": "^[a-z0-9]([a-z0-9_.-]*[a-z0-9])?$"
      },
      "additionalProperties": {
        "type": "string",
        "maxLength": 64
      }
    },
    "userData": {
      "description": "Any JSON value, null for none."
    },
    "allocation": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "required": [
            "id",
            "generation"
          ],
          "properties": {
            "id": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            },
            "generation": {
              "type": "integer"
            },
            "allocatedAt": {
              "type": "string",
              "format": "date-time",
              "description": "Optional; the agent stamps the time it first saw the allocation when absent."
            }
          }
        }
      ]
    },
    "draining": {
      "oneOf": [
        {
          "const": false
        },
        {
          "enum": [
            "stop",
            "destroy",
            "replace",
            "scale_down"
          ]
        }
      ],
      "description": "false, or the reason."
    },
    "contractVersion": {
      "const": 1
    },
    "generation": {
      "type": "integer",
      "minimum": 0,
      "description": "Bump on every change; the agent acknowledges it in status.observedGeneration after the files are written."
    },
    "publicEndpoints": {
      "type": "array",
      "description": "Optional override of the pod's ZAROZ_PUBLIC_ENDPOINTS, for an address assigned after pod creation.",
      "items": {
        "type": "object",
        "required": [
          "protocols",
          "internal_port",
          "address"
        ],
        "properties": {
          "protocols": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "internal_port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "address": {
            "type": "string"
          }
        }
      }
    }
  }
}
```

### `UnitSignal (units.zaroz.cloud/v1) status`

Written by the agent through the status subresource only, coalesced to at most 5 patches per second. Every field is always present (null, not absent) because the agent patches with a JSON merge patch.

[Archivo original: `unit-signal-status.schema.json`](/contract/units/v1/unit-signal-status.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/unit-signal-status.schema.json",
  "title": "UnitSignal (units.zaroz.cloud/v1) status",
  "description": "Written by the agent through the status subresource only, coalesced to at most 5 patches per second. Every field is always present (null, not absent) because the agent patches with a JSON merge patch.",
  "type": "object",
  "required": [
    "observedGeneration",
    "agentVersion",
    "signalledReady",
    "accepting",
    "activity",
    "activitySince",
    "shutdownRequestedAt",
    "allocationAck",
    "seq"
  ],
  "properties": {
    "observedGeneration": {
      "type": "integer"
    },
    "agentVersion": {
      "type": "string"
    },
    "signalledReady": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "format": "date-time"
        }
      ]
    },
    "accepting": {
      "type": "boolean"
    },
    "activity": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "enum": [
            "idle",
            "busy"
          ]
        }
      ]
    },
    "activitySince": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "format": "date-time"
        }
      ]
    },
    "shutdownRequestedAt": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "format": "date-time"
        }
      ]
    },
    "allocationAck": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string"
        }
      ],
      "description": "Id of the allocation whose files are written, null when none."
    },
    "seq": {
      "type": "integer",
      "minimum": 0,
      "description": "Monotonic per agent start."
    }
  }
}
```

### `/zaroz/unit.json`

Who this unit is. Labels update live; every other field is fixed for the life of the pod. Ignore fields you do not know.

[Archivo original: `unit.schema.json`](/contract/units/v1/unit.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/unit.schema.json",
  "title": "/zaroz/unit.json",
  "description": "Who this unit is. Labels update live; every other field is fixed for the life of the pod. Ignore fields you do not know.",
  "type": "object",
  "required": [
    "contract_version",
    "unit_id",
    "key",
    "labels",
    "origin",
    "workload",
    "project_id",
    "region",
    "private_name",
    "public_endpoints",
    "incarnation",
    "started_at"
  ],
  "properties": {
    "contract_version": {
      "const": 1
    },
    "unit_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "key": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40,
      "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
    },
    "labels": {
      "type": "object",
      "maxProperties": 16,
      "propertyNames": {
        "minLength": 1,
        "maxLength": 32,
        "pattern": "^[a-z0-9]([a-z0-9_.-]*[a-z0-9])?$"
      },
      "additionalProperties": {
        "type": "string",
        "maxLength": 64
      }
    },
    "origin": {
      "type": "string",
      "minLength": 1,
      "description": "How the unit came to exist (for example policy, code, warm)."
    },
    "workload": {
      "type": "object",
      "required": [
        "id",
        "slug"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "slug": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "project_id": {
      "type": "string",
      "minLength": 1
    },
    "region": {
      "type": "string",
      "minLength": 1
    },
    "private_name": {
      "type": "string",
      "minLength": 1,
      "description": "<key>.<workload>, resolvable from the project's other units."
    },
    "public_endpoints": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "protocols",
          "internal_port",
          "address"
        ],
        "properties": {
          "protocols": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "internal_port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "address": {
            "type": "string"
          }
        }
      }
    },
    "incarnation": {
      "type": "integer",
      "minimum": 0
    },
    "started_at": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### `/zaroz/user-data.json`

The customer's JSON value for this unit, or null. At most 64 KiB serialized compactly and nested at most 32 deep (enforced before it is delivered); any JSON value otherwise.

[Archivo original: `user-data.schema.json`](/contract/units/v1/user-data.schema.json)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://zaroz.cloud/contract/units/v1/user-data.schema.json",
  "title": "/zaroz/user-data.json",
  "description": "The customer's JSON value for this unit, or null. At most 64 KiB serialized compactly and nested at most 32 deep (enforced before it is delivered); any JSON value otherwise."
}
```

