Register an Account

accounts/register is a public operation — callable anonymously. Internally it orchestrates accounts/create, identities/create and accounts/connect as system calls, and returns a ready-to-use session:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7anonymous",
  "to": "accounts/register",
  "data": { "identifier": "ada", "secret": "seller-secret-001" }
}'
import { useAuthStore } from "~/stores/auth"

const auth = useAuthStore()

// wraps agent.send({ type: "command", to: "accounts/register", … })
await auth.register("ada", "seller-secret-001")
Response
200 OK
{
"type": "reply",
"from": "x:mK2nP4qR6sT8uV0w",
"to": "x:aB3dE5fG7hJ9kL1m…",
"data": {
  "token": "kX9…encrypted…Zw==",
  "role": "SELLER"
}
}

Connect (Login)

Same shape, different operation. The reply token is opaque to the client — it is attached to every subsequent message’s from field:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7anonymous",
  "to": "accounts/connect",
  "data": { "identifier": "reader", "secret": "buyer-secret-001" }
}'
await auth.connect("reader", "buyer-secret-001")
// store.set("auth/token", …) + agent identity applied
Response
200 OK
{
"type": "reply",
"data": {
  "token": "pQ4…encrypted…Yg==",
  "role": "BUYER"
}
}

Create a Catalog (Seller)

Requires the SELLER capability — the token in from now carries the seller’s identity. The catalog binds itself to the fiction rule set:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7<token>",
  "to": "catalogs/create",
  "data": { "name": "Science Fiction Shelf", "rules": "fiction" }
}'
const { createCatalog } = useCatalogs()

await createCatalog({
name: "Science Fiction Shelf",
rules: "fiction",
})
Response
200 OK
{
"type": "reply",
"data": {
  "success": true,
  "id": "ctl_92hf30a1"
}
}

Add a Book — Rejected by the Rules Engine

The fiction rule set requires an ISBN. This request omits it, so validateItem throws before anything is written — and the typed error travels to the client as a 400:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7<token>",
  "to": "books/add",
  "data": {
    "catalogId": "ctl_92hf30a1",
    "price": 12.99,
    "meta": { "title": "Dune", "author": "Frank Herbert" }
  }
}'
Response
400 INVALID
{
"type": "invalid",
"message": "Field \"isbn\" is required",
"errors": []
}

Add the ISBN and the same request succeeds:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7<token>",
  "to": "books/add",
  "data": {
    "catalogId": "ctl_92hf30a1",
    "price": 12.99,
    "meta": {
      "title": "Dune",
      "author": "Frank Herbert",
      "isbn": "978-0441172719",
      "genre": "sci-fi"
    }
  }
}'
const { addBook } = useBooks()

await addBook({
catalogId: "ctl_92hf30a1",
price: 12.99,
meta: {
  title: "Dune",
  author: "Frank Herbert",
  isbn: "978-0441172719",
  genre: "sci-fi",
},
})
Response
200 OK
{
"type": "reply",
"data": {
  "success": true,
  "id": "bk_7f3a2c9d"
}
}

List Books with Filters

A paginated read. On the client this is the useBooks composable — refs in the query key re-run it when page or filters change:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7<token>",
  "to": "books/list",
  "data": {
    "page": 1,
    "pageSize": 10,
    "filters": { "genre": "sci-fi" }
  }
}'
const { books, totalPages, applyFilters } = useBooks()

applyFilters({ genre: "sci-fi" })
// query re-runs; books/totalPages update reactively
Response
200 OK
{
"type": "reply",
"data": {
  "books": [
    {
      "id": "bk_7f3a2c9d",
      "price": 12.99,
      "meta": {
        "title": "Dune",
        "genre": "sci-fi"
      }
    }
  ],
  "total": 17,
  "totalPages": 2
}
}

Liveness Ping

The one message that skips the agent entirely — the transport echoes the data back, making it a dependency-free health probe:

curl -X POST http://localhost:3000/io \
-H "Content-Type: application/json" \
-d '{
  "type": "command",
  "from": "x:aB3dE5fG7hJ9kL1mN3pQ5rS7anonymous",
  "to": "io/ping",
  "data": { "echo": "ok" }
}'
Response
200 OK
{ "echo": "ok" }