> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talaria.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Generate an Ed25519 keypair, Talaria DID document, and signed registration request from Bash or Python

This quickstart creates a Talaria identity entirely on your machine. You generate an Ed25519 keypair, build a public DID document, sign a registration payload, and submit only the public data to a Directory.

<Warning>
  Your private key controls the identity. Never print it, commit it, include it in a request, or send it to the Directory.
</Warning>

## Prerequisites

Choose either the Bash or Python workflow.

<Tabs>
  <Tab title="Bash and OpenSSL">
    Install the following command-line tools:

    * Bash or another POSIX-compatible shell
    * OpenSSL 3 with Ed25519 support
    * Python 3 for Base58 encoding using only the standard library
    * `jq` for constructing JSON
    * `curl` for submitting the request

    Confirm that OpenSSL exposes Ed25519:

    ```bash theme={null}
    openssl list -public-key-algorithms | grep -i ED25519
    ```

    If this command returns no match, install or select OpenSSL 3 before continuing. The LibreSSL version bundled with some operating systems may not support the commands in this guide.
  </Tab>

  <Tab title="Python">
    Install Python 3.9 or later, then create an isolated environment:

    ```bash theme={null}
    python3 -m venv .talaria-venv
    source .talaria-venv/bin/activate
    python -m pip install --upgrade pip
    python -m pip install cryptography
    ```

    The Python workflow uses `cryptography` for Ed25519 key generation and signing. Base58 encoding and JSON generation use the Python standard library.
  </Tab>
</Tabs>

## Set the Directory URL

Replace the placeholder with the HTTPS URL supplied by your Directory operator:

```bash theme={null}
export TALARIA_DIRECTORY_URL="https://directory.example.com"
```

The generation steps do not contact the Directory. You use this URL only when you submit or resolve the DID.

## Generate the identity

<Tabs>
  <Tab title="Bash and OpenSSL">
    Create `generate-talaria-identity.sh` with the following content:

    ```bash theme={null}
    #!/usr/bin/env bash
    set -euo pipefail

    umask 077
    IDENTITY_DIR="${1:-talaria-identity}"

    for required_command in openssl jq python3; do
      if ! command -v "$required_command" >/dev/null 2>&1; then
        echo "Missing required command: $required_command" >&2
        exit 1
      fi
    done

    if ! openssl list -public-key-algorithms 2>/dev/null | grep -qi ED25519; then
      echo "OpenSSL does not report Ed25519 support. Install or select OpenSSL 3." >&2
      exit 1
    fi

    mkdir -p "$IDENTITY_DIR"
    chmod 700 "$IDENTITY_DIR"

    base58btc() {
      python3 -c '
    import sys

    alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
    data = sys.stdin.buffer.read()
    number = int.from_bytes(data, "big")
    encoded = ""

    while number:
        number, remainder = divmod(number, 58)
        encoded = alphabet[remainder] + encoded

    leading_zeroes = len(data) - len(data.lstrip(b"\x00"))
    sys.stdout.write("1" * leading_zeroes + encoded)
    '
    }

    # Create the Ed25519 private and public keys.
    openssl genpkey \
      -algorithm ED25519 \
      -out "$IDENTITY_DIR/private-key.pem"

    openssl pkey \
      -in "$IDENTITY_DIR/private-key.pem" \
      -pubout \
      -out "$IDENTITY_DIR/public-key.pem"

    # An OpenSSL 3 Ed25519 SubjectPublicKeyInfo value ends with the raw 32-byte key.
    openssl pkey \
      -in "$IDENTITY_DIR/private-key.pem" \
      -pubout \
      -outform DER \
      | tail -c 32 > "$IDENTITY_DIR/public-key.raw"

    PUBLIC_KEY_BYTES="$(wc -c < "$IDENTITY_DIR/public-key.raw" | tr -d " ")"
    if [ "$PUBLIC_KEY_BYTES" != "32" ]; then
      echo "Expected a 32-byte Ed25519 public key, received $PUBLIC_KEY_BYTES bytes." >&2
      exit 1
    fi

    # Create a random 128-bit identifier and encode it with the Base58 alphabet.
    openssl rand 16 > "$IDENTITY_DIR/did-entropy.bin"
    DID_IDENTIFIER="$(base58btc < "$IDENTITY_DIR/did-entropy.bin")"
    DID="did:talaria:$DID_IDENTIFIER"
    KEY_ID="$DID#root"
    PUBLIC_KEY_MULTIBASE="z$(base58btc < "$IDENTITY_DIR/public-key.raw")"

    # Build the public DID document.
    jq -n \
      --arg did "$DID" \
      --arg key_id "$KEY_ID" \
      --arg public_key "$PUBLIC_KEY_MULTIBASE" \
      '{
        "@context": "https://www.w3.org/ns/did/v1",
        id: $did,
        verificationMethod: [
          {
            id: $key_id,
            type: "Ed25519VerificationKey2020",
            controller: $did,
            publicKeyMultibase: $public_key
          }
        ],
        authentication: [$key_id],
        keyAgreement: [],
        service: []
      }' > "$IDENTITY_DIR/did-document.json"

    # Sign the public registration payload: DID, a pipe, and the exact timestamp.
    TIMESTAMP="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
    printf '%s' "$DID|$TIMESTAMP" > "$IDENTITY_DIR/registration-payload.txt"

    openssl pkeyutl \
      -sign \
      -rawin \
      -inkey "$IDENTITY_DIR/private-key.pem" \
      -in "$IDENTITY_DIR/registration-payload.txt" \
      -out "$IDENTITY_DIR/registration-signature.bin"

    SIGNATURE="$(openssl base64 -A -in "$IDENTITY_DIR/registration-signature.bin")"

    jq -n \
      --arg did "$DID" \
      --arg timestamp "$TIMESTAMP" \
      --arg signature "$SIGNATURE" \
      --arg root_pubkey "$PUBLIC_KEY_MULTIBASE" \
      --slurpfile document "$IDENTITY_DIR/did-document.json" \
      '{
        did: $did,
        timestamp: $timestamp,
        signature: $signature,
        root_pubkey: $root_pubkey,
        did_document: $document[0]
      }' > "$IDENTITY_DIR/registration.json"

    printf 'DID: %s\n' "$DID"
    printf 'DID document: %s/did-document.json\n' "$IDENTITY_DIR"
    printf 'Registration request: %s/registration.json\n' "$IDENTITY_DIR"
    printf 'Private key: %s/private-key.pem\n' "$IDENTITY_DIR"
    ```

    Make the script executable and run it:

    ```bash theme={null}
    chmod +x generate-talaria-identity.sh
    ./generate-talaria-identity.sh
    ```
  </Tab>

  <Tab title="Python">
    Create `generate_talaria_identity.py` with the following content:

    ```python theme={null}
    #!/usr/bin/env python3
    import argparse
    import base64
    import json
    import os
    import secrets
    from datetime import datetime, timezone
    from pathlib import Path

    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey


    BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"


    def base58btc(data: bytes) -> str:
        number = int.from_bytes(data, "big")
        encoded = ""

        while number:
            number, remainder = divmod(number, 58)
            encoded = BASE58_ALPHABET[remainder] + encoded

        leading_zeroes = len(data) - len(data.lstrip(b"\x00"))
        return "1" * leading_zeroes + encoded


    def write_json(path: Path, value: dict) -> None:
        path.write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8")


    def main() -> None:
        parser = argparse.ArgumentParser(
            description="Generate a Talaria DID, Ed25519 keypair, and registration request."
        )
        parser.add_argument(
            "--output",
            default="talaria-identity",
            help="Directory for generated files (default: talaria-identity)",
        )
        args = parser.parse_args()

        output = Path(args.output)
        output.mkdir(mode=0o700, parents=True, exist_ok=True)
        os.chmod(output, 0o700)

        private_key = Ed25519PrivateKey.generate()
        public_key = private_key.public_key()

        private_pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption(),
        )
        public_pem = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo,
        )
        public_raw = public_key.public_bytes(
            encoding=serialization.Encoding.Raw,
            format=serialization.PublicFormat.Raw,
        )

        private_path = output / "private-key.pem"
        private_path.write_bytes(private_pem)
        os.chmod(private_path, 0o600)
        (output / "public-key.pem").write_bytes(public_pem)
        (output / "public-key.raw").write_bytes(public_raw)

        did_identifier = base58btc(secrets.token_bytes(16))
        did = f"did:talaria:{did_identifier}"
        key_id = f"{did}#root"
        public_key_multibase = "z" + base58btc(public_raw)

        document = {
            "@context": "https://www.w3.org/ns/did/v1",
            "id": did,
            "verificationMethod": [
                {
                    "id": key_id,
                    "type": "Ed25519VerificationKey2020",
                    "controller": did,
                    "publicKeyMultibase": public_key_multibase,
                }
            ],
            "authentication": [key_id],
            "keyAgreement": [],
            "service": [],
        }

        timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        payload = f"{did}|{timestamp}".encode("utf-8")
        signature = base64.b64encode(private_key.sign(payload)).decode("ascii")

        registration = {
            "did": did,
            "timestamp": timestamp,
            "signature": signature,
            "root_pubkey": public_key_multibase,
            "did_document": document,
        }

        write_json(output / "did-document.json", document)
        write_json(output / "registration.json", registration)
        (output / "registration-payload.txt").write_bytes(payload)

        print(f"DID: {did}")
        print(f"DID document: {output / 'did-document.json'}")
        print(f"Registration request: {output / 'registration.json'}")
        print(f"Private key: {private_path}")


    if __name__ == "__main__":
        main()
    ```

    Run the CLI:

    ```bash theme={null}
    python generate_talaria_identity.py
    ```

    To choose a different output directory:

    ```bash theme={null}
    python generate_talaria_identity.py --output my-talaria-identity
    ```
  </Tab>
</Tabs>

## Review the generated files

Both workflows create the same artifacts:

<Tree />

| File                       | Purpose                                             | Safe to publish? |
| -------------------------- | --------------------------------------------------- | ---------------- |
| `private-key.pem`          | Signs requests that control the DID.                | No               |
| `public-key.pem`           | Standard PEM representation of the public key.      | Yes              |
| `public-key.raw`           | Raw 32-byte Ed25519 public key.                     | Yes              |
| `did-document.json`        | Public DID document submitted to the Directory.     | Yes              |
| `registration-payload.txt` | Exact public payload signed for registration.       | Yes              |
| `registration.json`        | Complete public registration request and signature. | Yes              |

Inspect the DID document and registration body before submission:

```bash theme={null}
jq . talaria-identity/did-document.json
jq . talaria-identity/registration.json
```

Confirm that these values agree:

* `registration.json.did` equals `did_document.id`.
* The first `verificationMethod.id` starts with the same DID.
* `controller` equals the DID.
* `root_pubkey` equals the first `publicKeyMultibase` value.
* `registration.json` does not contain the private key.

## Register the DID

Submit the generated request:

```bash theme={null}
curl --fail-with-body \
  --request POST \
  --url "$TALARIA_DIRECTORY_URL/register-did" \
  --header "Content-Type: application/json" \
  --data @talaria-identity/registration.json
```

A successful submission returns `202 Accepted` with a transaction identifier and a pending registration status:

```json theme={null}
{
  "status": "did registration pending",
  "registration_status": "pending",
  "tx_id": "<transaction-id>",
  "did": "did:talaria:<identifier>",
  "did_document": {
    "id": "did:talaria:<identifier>"
  }
}
```

<Note>
  `202 Accepted` means the Directory accepted the registration for processing. It does not mean the registration is confirmed.
</Note>

## Resolve the DID

Read the DID from the registration file and URL-encode it for the path:

```bash theme={null}
DID="$(jq -r '.did' talaria-identity/registration.json)"
ENCODED_DID="$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$DID")"
```

Resolve the public record:

```bash theme={null}
curl --fail-with-body \
  --url "$TALARIA_DIRECTORY_URL/resolve/$ENCODED_DID" \
  | jq .
```

While registration is pending, the Directory may return `425` with `recipient_registration_pending`. Retry after the registration settles. A confirmed record returns `200` and includes:

```json theme={null}
{
  "document": {
    "id": "did:talaria:<identifier>"
  },
  "registration_status": "confirmed"
}
```

## Protect the private key

After registration:

1. Move `private-key.pem` into your secret manager or encrypted key store.
2. Keep at least one secure recovery copy.
3. Do not commit the generated directory to source control.
4. Delete unneeded binary intermediates after you confirm your backup and signing workflow.

Add the generated directory to `.gitignore`:

```gitignore theme={null}
talaria-identity/
```

<Card title="Explore the Directory API" icon="book-open" href="/talaria-directory-api">
  Continue with alias, device, DID document, and delivery policy endpoints.
</Card>
