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
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
{: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/healthPOST /internal/keyring/create-walletPOST /internal/keyring/has-walletPOST /internal/keyring/get-addressPOST /internal/keyring/sign-messagePOST /internal/keyring/sign-raw-messagePOST /internal/keyring/sign-transactionPOST /internal/keyring/sign-authorization
Every non-health route requires:
x-keyring-timestampx-keyring-request-idx-keyring-signature
Build those headers with:
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
client =
SiwaKeyring.Client.new(
base_url: "https://siwa.internal",
secret: "proxy-secret"
)
{:ok, %{"address" => address}} = SiwaKeyring.Client.get_address(client)Development
mix test
mix format --check-formatted