Developer console

Committing approval rules

Commit a change to the Org's approval rules with passkeys from two or more people.

The base URL is https://api.clavis.nexus and the path is /orgs/{org}/approval-requests. Authorisation is the session JWT plus a SignProof in the body (see Authentication).

Steps

  1. 1. Open the organisation (Org) and set two or more committers at /developers/{orgId}/settings/governance.

  2. 2. An owner or admin opens a request with POST …/approval-requests.

    Request
    POST https://api.clavis.nexus/orgs/<org>/approval-requests
    Authorization: Bearer <session JWT>
    Content-Type: application/json
    
    {
      "request_kind": "policy_revision",
      "policy_revision_hash": "<64 hex>",
      "target_domain": "<domain>",
      "sign_proof": {
        "v": 1,
        "purpose": "org.approval.request",
        "intent_type": "generic_sign",
        "intent_id": "<intent_id>",
        "rp_id": "<rp id>",
        "origin": "https://<host>",
        "counter_previous": 0,
        "public_key_cose_b64u": "<base64url>",
        "message_digest": "<base64url>",
        "sub": "<passkey sub>",
        "challenge": "<base64url>",
        "binding_nonce": "<base64url>",
        "credential_id": "<base64url>",
        "authenticator_data": "<base64url>",
        "client_data_json": "<base64url>",
        "signature": "<base64url>",
        "signed_at": 1710000000
      }
    }
    • policy_revision_hash
      From the response of POST /orgs/<org>/policy-revisions. The current rules return POLICY_UNCHANGED; an unknown hash returns UNKNOWN_POLICY.
    • target_domain
      Trim it, lowercase it and drop any trailing dot. Then take SHA-256 of the digest input as UTF-8 and encode it as Base64URL. Do not add whitespace.
    • request_id
      Used for the votes and the commit that follow.
    • sign_proof
      With purpose org.approval.request, obtained after a passkey signature in the developer console.

    Response

    {
      "request_id": "<id>",
      "expires_at": 1710000900
    }

    Digest input

    {"created_by_sub":"<session sub>","org_id":"<org UUID>","policy_revision_hash":"<64 hex>","request_kind":"policy_revision","target_domain":"<domain>"}
  3. 3. Each voter needed for the commit votes with POST …/votes. The body is a sign_proof with purpose org.policy.vote.

    Request
    POST https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>/votes
    Authorization: Bearer <session JWT>
    Content-Type: application/json
    
    {
      "sign_proof": {
        "v": 1,
        "purpose": "org.policy.vote",
        "intent_type": "generic_sign",
        "intent_id": "<intent_id>",
        "rp_id": "<rp id>",
        "origin": "https://<host>",
        "counter_previous": 0,
        "public_key_cose_b64u": "<base64url>",
        "message_digest": "<base64url>",
        "sub": "<passkey sub>",
        "challenge": "<base64url>",
        "binding_nonce": "<base64url>",
        "credential_id": "<base64url>",
        "authenticator_data": "<base64url>",
        "client_data_json": "<base64url>",
        "signature": "<base64url>",
        "signed_at": 1710000000
      }
    }

    Response

    {
      "ok": true,
      "state": "<state>"
    }

    Digest input

    {"approval_request_id":"<request_id>","policy_revision_hash":"<64 hex>","voter_sub":"<session sub>"}
  4. 4. Once the threshold is reached, commit with POST …/commit. The body is a sign_proof with purpose org.approval.commit.

    Request
    POST https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>/commit
    Authorization: Bearer <session JWT>
    Content-Type: application/json
    
    {
      "sign_proof": {
        "v": 1,
        "purpose": "org.approval.commit",
        "intent_type": "generic_sign",
        "intent_id": "<intent_id>",
        "rp_id": "<rp id>",
        "origin": "https://<host>",
        "counter_previous": 0,
        "public_key_cose_b64u": "<base64url>",
        "message_digest": "<base64url>",
        "sub": "<passkey sub>",
        "challenge": "<base64url>",
        "binding_nonce": "<base64url>",
        "credential_id": "<base64url>",
        "authenticator_data": "<base64url>",
        "client_data_json": "<base64url>",
        "signature": "<base64url>",
        "signed_at": 1710000000
      }
    }

    Response

    {
      "ok": true,
      "org_status": "<status>",
      "approval_manifest": null
    }

    Digest input

    {"committer_sub":"<session sub>","org_id":"<org UUID>","request_id":"<request_id>"}

Endpoints

The HTTP for approval-requests, and reading a request.

  • POST …/approval-requests
    Opens a request. Needs Authorization and a sign_proof.
  • GET …/approval-requests/{id}
    Reads the vote totals and ready_for_finalize.
  • POST …/votes
    Adds one vote.
  • POST …/commit
    Commits the change.
GET
GET https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>
Authorization: Bearer <session JWT>

Response

{
  "request": {},
  "vote_count": 1,
  "weight_sum": 1,
  "ready_for_finalize": false
}

Scope

  • Use these steps when the Org has two or more committers.
  • Votes are cast in the developer console.

Reference

  • Authentication — The session JWT and SignProof purposes.
  • Quickstart — Setting up the Org, the app and the host check.