Siwa

Shared Elixir library for Regent SIWA flows.

Use this package when a Regent service needs to:

The shared HTTP contract lives in the Regent shared services OpenAPI file. Keep that contract as the source of truth for public request and response shapes.

Build A Message

message =
  Siwa.build_message(%{
    domain: "regent.cx",
    address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
    uri: "https://regent.cx/v1/agent/siwa/verify",
    agent_id: 77,
    agent_registry: "eip155:8453:0x3333333333333333333333333333333333333333",
    chain_id: 8453,
    nonce: "nonce1234",
    issued_at: DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
  })

Issue And Consume A Nonce

{:ok, nonce} =
  Siwa.create_nonce(%{
    address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
    agent_id: 77,
    agent_registry: "eip155:8453:0x3333333333333333333333333333333333333333",
    audience: "regent"
  })

{:ok, _stored_nonce} =
  Siwa.verify_nonce(%{
    address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
    agent_id: 77,
    agent_registry: "eip155:8453:0x3333333333333333333333333333333333333333",
    audience: "regent",
    nonce: nonce.nonce
  })

Verify Sign-In

{:ok, result} =
  Siwa.verify(message, signature,
    audience: "regent",
    domain: "regent.cx",
    required_services: ["MCP"],
    required_trust_models: ["reputation"]
  )

case result.status do
  "authenticated" -> result.receipt
  "not_registered" -> result.action
  "rejected" -> result.reason
end

Create And Verify A Receipt

{:ok, receipt} =
  Siwa.create_receipt(%{
    "typ" => "siwa_receipt",
    "sub" => "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
    "chain_id" => 8453,
    "registry_address" => "0x3333333333333333333333333333333333333333",
    "token_id" => "77",
    "aud" => "platform"
  })

{:ok, claims} = Siwa.verify_receipt(receipt.token, audience: "platform")

Development

mix test
mix format --check-formatted