Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,13 @@ The conformance command intentionally treats unknown schemas, malformed JSON,
and empty fixture sets as harness failures. Invalid fixtures must fail because
the schema rejected them, not because the fixture cannot be parsed.

## Infra Service Entrypoints

Service-capable infra repos should declare supported, deferred, or unsupported
HTTP/MCP service surfaces in `hasna.contract.json` instead of adding `*-serve`
bins mechanically. The current Worker 2 baseline matrix is in
[`docs/infra-service-entrypoint-matrix.md`](docs/infra-service-entrypoint-matrix.md).

## Verification

```bash
Expand Down
55 changes: 55 additions & 0 deletions docs/infra-service-entrypoint-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Infra Service Entrypoint Matrix

Date: 2026-07-06

Source tasks:

- `0c8caca6-c564-4ad3-a878-23a8405ca2bc` - Add infra service binaries and deployment contracts.
- `reports/task-proposals/adversarial-12/reviewer-02-open-infra.md` proposal 4.
- `reports/task-proposals/adversarial-12/reviewer-09-service-baseline.md` SVC-01 through SVC-03.
- `reports/task-proposals/adversarial-12/reviewer-12-final-dedupe.md` task 17.

This matrix is intentionally conservative. A `*-serve` package bin is only
`supported` when the repo already exposes a service boundary that can declare
health/version routes, auth behavior, deployment modes, and readiness gates in
`hasna.contract.json`. Repos with raw secret values, undefined hosted auth, or
missing state ownership must use `deferred` instead of publishing a broad server
bin.

| Repo | Package | Current bins | Service decision | Required next contract evidence |
| --- | --- | --- | --- | --- |
| `open-backup` | `@hasna/backup@0.1.2` | `backup`, `backup-mcp` | `deferred` | Define hosted state owner, backup target credential refs, `/health`, `/ready`, redacted inventory output, and restore-smoke evidence before `backup-serve`. |
| `open-bridge` | `@hasna/bridge@0.2.1` | `bridge`, `bridge-mcp` | `deferred` | Define bridge auth scopes, connector secret refs, event replay boundaries, and no-secret output gates before `bridge-serve`. |
| `open-domains` | `@hasna/domains@0.0.27` | `domains`, `domains-mcp`, `domains-serve` | `supported` | Add/refresh `hasna.contract.json` service surface with `local` and `self-hosted` support, `/health`, `/ready`, `/version`, `/v1`, Route53/Cloudflare secret refs, dry-run DNS mutation gates, and redaction tests. |
| `open-hooks` | `@hasna/hooks@0.2.20` | `hooks` | `deferred` | Decide whether hooks is CLI-only, MCP-capable, or service-capable; service mode needs webhook signature/replay gates and operator-visible DLQ before `hooks-serve`. |
| `open-machines` | `@hasna/machines@0.0.63` | `machines`, `machines-mcp`, `machines-agent`, `machines-serve` | `supported` | Add/refresh service surface with lease/claim auth scopes, private metadata redaction, `/v1` ownership boundaries, and fleet dry-run fixture gates. |
| `open-releases` | `@hasna/releases@0.1.0` | `releases`, `releases-mcp` | `deferred` | Promote release evidence schema and append-only ledger first; then add `releases-serve` with package/version/gate/evidence APIs and unauthorized mutation denial. |
| `open-secrets` | `@hasna/secrets@0.1.33` | `secrets`, `secrets-mcp`, `secrets-serve` | `deferred` for hosted raw-value access | Hosted service surfaces must declare secret-reference and lease semantics, local-only reveal exclusions, audit gates, and tests proving HTTP/MCP never returns raw secret values. |
| `open-servers` | `@hasna/servers@0.1.21` | `servers`, `servers-mcp` | `deferred` | Define lifecycle locks, operation ids, job-scoped auth, command/env redaction, and registered-server boundaries before `servers-serve`. |
| `open-uptime` | `@hasna/uptime@0.1.69` | `uptime`, `uptime-mcp` | `deferred` pending standard bin | Existing `serve` command should either publish `uptime-serve` or declare `serve: unsupported`; contract needs probe storage readiness and redacted status gates. |
| `open-gateway` | `@hasna/gateway@0.1.3` | `gateway`, `gateway-mcp` | `deferred` until gateway runtime task completes | Add `gateway-serve` only with auth, secret references, budget/ledger readiness gates, redacted diagnostics, CORS policy, and mock chat smoke. |
| `open-monitor` | `@hasna/monitor@0.1.24` | `monitor`, `monitor-mcp`, `monitor-server`, `monitor-web` | `supported with alias decision` | Either add `monitor-serve` as an alias or declare `monitor-server` as the canonical exception; service surface must include health/readiness/version and redacted machine metadata. |

## Contract fields to use

Use `hasna.service_contract.v1` with:

- `deploymentModes`: one or more of `local`, `self-hosted`, `cloud`. Do not use
`remote` as a deployment mode.
- `serviceSurfaces[]`: one record per HTTP/MCP service boundary.
- `serviceSurfaces[].status`: `supported`, `deferred`, or `unsupported`.
- `serviceSurfaces[].deferReason`: required for `deferred` and `unsupported`.
- `serviceSurfaces[].readinessGates[]`: auth, storage, secret-ref, migration,
health, readiness, redaction, smoke, and operator gates with command/evidence
status when available.

## First implementation tasks

1. Apply this contract to `open-domains`, `open-machines`, and `open-monitor`
first because they already expose service/server bins.
2. Complete the separate `open-gateway` runtime task before declaring
`gateway-serve` supported.
3. Keep `open-secrets` hosted service deferred until raw-value reveal is
strictly local-only and network surfaces return refs/leases only.
4. Create app-specific tasks for `open-releases`, `open-servers`, and
`open-uptime` once the service boundary and readiness owner are agreed.
32 changes: 32 additions & 0 deletions examples/service-contract.valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@
"kitVersion": "0.3.0",
"description": "Universal task management for AI coding agents.",
"bins": ["todos", "todos-mcp", "todos-serve"],
"deploymentModes": ["local", "self-hosted"],
"serviceSurfaces": [
{
"name": "http",
"status": "supported",
"bin": "todos-serve",
"mcpBin": "todos-mcp",
"authMode": "api-key",
"deploymentModes": ["local", "self-hosted"],
"health": { "method": "GET", "path": "/health", "public": true },
"readiness": { "method": "GET", "path": "/ready", "public": false },
"version": { "method": "GET", "path": "/version", "public": true },
"apiBasePath": "/v1",
"readinessGates": [
{
"id": "auth-negative-smoke",
"kind": "auth",
"required": true,
"command": "todos serve --port 0 && curl /v1/tasks",
"status": "pending",
"summary": "Unauthenticated non-health routes must fail closed before self-hosted readiness."
},
{
"id": "redacted-output-scan",
"kind": "redaction",
"required": true,
"status": "pending",
"summary": "Health, readiness, logs, and CLI JSON must not expose credential values."
}
]
}
],
"storage": {
"mode": "cloud",
"envPrefix": "HASNA_TODOS_",
Expand Down
252 changes: 247 additions & 5 deletions src/hasna.contract.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
"description": "Repo self-description (hasna.contract.json) for the Hasna Service Contract v1. Storage runtime enum is local|cloud ONLY per Amendment A1 (PURE REMOTE).",
"type": "object",
"additionalProperties": false,
"required": ["schema", "name", "class", "contractVersion", "kitVersion"],
"required": [
"schema",
"name",
"class",
"contractVersion",
"kitVersion"
],
"properties": {
"$schema": {
"type": "string",
Expand All @@ -20,7 +26,12 @@
"description": "Lowercase dashed app short-name, e.g. todos, mailery, loops."
},
"class": {
"enum": ["library", "cli-with-store", "service", "saas"]
"enum": [
"library",
"cli-with-store",
"service",
"saas"
]
},
"contractVersion": {
"const": "v1"
Expand All @@ -36,16 +47,247 @@
},
"bins": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"items": {
"type": "string",
"minLength": 1
},
"description": "Declared bins. Allowlisted: <name>, <name>-cli, <name>-mcp, <name>-serve, <name>-worker, <name>-runner, <name>-daemon, <name>-migrate, <name>-doctor."
},
"deploymentModes": {
"type": "array",
"items": {
"enum": [
"local",
"self-hosted",
"cloud"
]
},
"description": "Supported deployment modes. local = this machine, self-hosted = Hasna-owned AWS, cloud = multi-tenant SaaS for outside users."
},
"serviceSurfaces": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"status",
"authMode",
"deploymentModes"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"status": {
"enum": [
"supported",
"deferred",
"unsupported"
]
},
"bin": {
"type": "string",
"minLength": 1
},
"mcpBin": {
"type": "string",
"minLength": 1
},
"authMode": {
"enum": [
"none",
"local-only",
"api-key",
"session",
"service-token",
"custom"
]
},
"deploymentModes": {
"type": "array",
"items": {
"enum": [
"local",
"self-hosted",
"cloud"
]
},
"minItems": 1
},
"health": {
"type": "object",
"additionalProperties": false,
"required": [
"method",
"path"
],
"properties": {
"method": {
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
},
"path": {
"type": "string",
"pattern": "^/[A-Za-z0-9_./:*-]*$"
},
"public": {
"type": "boolean"
},
"description": {
"type": "string",
"minLength": 1
}
}
},
"readiness": {
"type": "object",
"additionalProperties": false,
"required": [
"method",
"path"
],
"properties": {
"method": {
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
},
"path": {
"type": "string",
"pattern": "^/[A-Za-z0-9_./:*-]*$"
},
"public": {
"type": "boolean"
},
"description": {
"type": "string",
"minLength": 1
}
}
},
"version": {
"type": "object",
"additionalProperties": false,
"required": [
"method",
"path"
],
"properties": {
"method": {
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
},
"path": {
"type": "string",
"pattern": "^/[A-Za-z0-9_./:*-]*$"
},
"public": {
"type": "boolean"
},
"description": {
"type": "string",
"minLength": 1
}
}
},
"apiBasePath": {
"type": "string",
"pattern": "^/v[0-9]+$"
},
"openApiPath": {
"type": "string",
"pattern": "^/[A-Za-z0-9_./:-]*$"
},
"deferReason": {
"type": "string",
"minLength": 1
},
"readinessGates": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"kind": {
"enum": [
"auth",
"storage",
"secret-ref",
"migration",
"health",
"readiness",
"redaction",
"smoke",
"operator",
"other"
]
},
"required": {
"type": "boolean"
},
"command": {
"type": "string",
"minLength": 1
},
"evidenceRef": {
"type": "object"
},
"status": {
"enum": [
"pending",
"passed",
"failed",
"blocked",
"deferred"
]
},
"summary": {
"type": "string",
"minLength": 1
}
}
}
}
}
},
"description": "Declared HTTP/MCP service surfaces. Supported surfaces name lifecycle endpoints; unsafe or unfinished surfaces use deferred/unsupported with a reason."
},
"storage": {
"type": "object",
"additionalProperties": false,
"required": ["mode"],
"required": [
"mode"
],
"properties": {
"mode": {
"enum": ["local", "cloud"],
"enum": [
"local",
"cloud"
],
"description": "Runtime storage enum. local|cloud ONLY (Amendment A1: PURE REMOTE)."
},
"envPrefix": {
Expand Down
Loading
Loading