# SiwaKeyring
Isolated signer service and Elixir client for Regent SIWA.
Use this package when a Regent process needs a local wallet for SIWA receipts, request signatures, transaction payloads, or authorization payloads.
## Configure The Wallet Store
```elixir
config :siwa_keyring,
path: "/data/siwa-keyring.json",
password: System.fetch_env!("KEYSTORE_PASSWORD"),
secret: System.fetch_env!("KEYRING_PROXY_SECRET")
```
The wallet file is encrypted with AES-256-GCM. The proxy secret signs requests to the keyring routes.
## Local Service Calls
```elixir
{:ok, wallet} = SiwaKeyring.create_wallet()
{:ok, %{has_wallet: true}} = SiwaKeyring.has_wallet?()
{:ok, address} = SiwaKeyring.get_address()
{:ok, signature} = SiwaKeyring.sign_message("Sign in to Regent")
{:ok, raw_signature} = SiwaKeyring.sign_raw_message("payload-to-bind")
```
## HTTP Routes
Run `SiwaKeyring.Router` at your internal service root.
Available routes:
- `GET /internal/keyring/health`
- `POST /internal/keyring/create-wallet`
- `POST /internal/keyring/has-wallet`
- `POST /internal/keyring/get-address`
- `POST /internal/keyring/sign-message`
- `POST /internal/keyring/sign-raw-message`
- `POST /internal/keyring/sign-transaction`
- `POST /internal/keyring/sign-authorization`
Every non-health route requires:
- `x-keyring-timestamp`
- `x-keyring-request-id`
- `x-keyring-signature`
Build those headers with:
```elixir
body = Jason.encode!(%{"message" => "Sign in to Regent"})
headers =
SiwaKeyring.Auth.compute_hmac(
"proxy-secret",
"POST",
"/internal/keyring/sign-message",
body
)
```
The request id is included in the signed payload and can only be used once during the timestamp freshness window.
Transaction and authorization signing accepts the shared wallet-action envelope from `regent-services-contract.openapiv3.yaml`: `chain_id`, `to`, `value`, `data`, `expected_signer`, `expires_at`, `risk_copy`, and `idempotency_key`.
## Remote Client
```elixir
client =
SiwaKeyring.Client.new(
base_url: "https://siwa.internal",
secret: "proxy-secret"
)
{:ok, %{"address" => address}} = SiwaKeyring.Client.get_address(client)
```
## Development
```bash
mix test
mix format --check-formatted
```