Aether Lexicon

ATProto Lexicon for Elixir.

Note: this is a work in progress. Currently only doing validation but will likely have more features in the future.

Validation Features:

Installation

Add aether_lexicon to your list of dependencies in mix.exs:

def deps do
  [
    {:aether_lexicon, "~> 0.1.1"}
  ]
end

Quick Start

Basic Validation

alias Aether.ATProto.Lexicon.Validation

# Define a schema
schema = %{
  "lexicon" => 1,
  "id" => "com.example.post",
  "defs" => %{
    "main" => %{
      "type" => "record",
      "record" => %{
        "type" => "object",
        "required" => ["text", "createdAt"],
        "properties" => %{
          "text" => %{
            "type" => "string",
            "maxLength" => 300
          },
          "createdAt" => %{
            "type" => "string",
            "format" => "datetime"
          }
        }
      }
    }
  }
}

# Validate data
data = %{
  "text" => "Hello, ATProto!",
  "createdAt" => "2024-01-01T00:00:00Z"
}

case Validation.validate(schema, "main", data) do
  {:ok, validated_data} ->
    # Data is valid and normalized
    IO.puts("Valid: #{inspect(validated_data)}")

  {:error, message} ->
    # Validation failed
    IO.puts("Error: #{message}")
end

XRPC Endpoint Validation

# Query endpoint schema
query_schema = %{
  "lexicon" => 1,
  "id" => "com.example.getPosts",
  "defs" => %{
    "main" => %{
      "type" => "query",
      "parameters" => %{
        "type" => "params",
        "properties" => %{
          "limit" => %{"type" => "integer", "minimum" => 1, "maximum" => 100}
        }
      },
      "output" => %{
        "encoding" => "application/json",
        "schema" => %{
          "type" => "object",
          "properties" => %{
            "posts" => %{"type" => "array", "items" => %{"type" => "string"}}
          }
        }
      }
    }
  }
}

# Validate parameters
{:ok, params} = Validation.validate_parameters(query_schema, "main", %{"limit" => "50"})

# Validate output
{:ok, output} = Validation.validate_output(query_schema, "main", %{
  "posts" => ["Post 1", "Post 2"]
})

String Format Validation

# DIDs (Decentralized Identifiers)
{:ok, did} = Validation.validate(did_schema, "main", "did:plc:abc123xyz")

# Handles
{:ok, handle} = Validation.validate(handle_schema, "main", "user.bsky.social")

# NSIDs (Namespace IDs)
{:ok, nsid} = Validation.validate(nsid_schema, "main", "com.atproto.server.createSession")

# Datetime (ISO 8601 / RFC 3339)
{:ok, dt} = Validation.validate(datetime_schema, "main", "2024-01-01T12:00:00Z")

Supported Types

Primitive Types

String Formats

Complex Types

IPLD Types

Special Types

Top-Level Types

XRPC Validation Functions

AetherLexicon provides dedicated functions for validating different parts of XRPC endpoints:

Documentation

Comparison with Official Library

AetherLexicon provides 100% validation parity with the official TypeScript @atproto/lexicon library. In fact, we provide additional functionality (error validation) that the official library doesn't have.

The main difference is architectural:

See docs/OFFICIAL_LEXICON_COMPARISON.md for a detailed comparison.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Apache License - see LICENSE.md for details.

Acknowledgments

This library implements the ATProto Lexicon specification created by Bluesky PBLLC. The validation logic is based on the official TypeScript implementation at @atproto/lexicon.