Packages

Sign and verify HTTP requests and responses in Elixir: RFC 9421 HTTP Message Signatures, Web Bot Auth for AI agents, JWS/JWE, digests and replay protection, with Plug (including Phoenix), Req, Finch and Ash integrations.

Current section

Files

Jump to
Raw

mix.exs

defmodule RequestSeal.MixProject do
  use Mix.Project

  # Reviewed public guides: one inventory for Hex and ExDoc. No directory exports.
  @public_documents [
    "README.md",
    "CONTRIBUTING.md",
    "SECURITY.md",
    "CHANGELOG.md",
    "LICENSE",
    "NOTICE",
    "docs/adr/0001-library-boundary.md",
    "docs/adr/0002-lossless-http-and-structured-fields.md",
    "docs/adr/0003-source-bound-profiles.md",
    "docs/adr/0004-key-custody-and-discovery.md",
    "docs/adr/0005-atomic-replay-envelope.md",
    "docs/adr/0006-verification-results-and-observability.md",
    "docs/adr/0007-framework-and-proxy-adapters.md",
    "docs/adr/0008-toolchain-and-compatibility.md",
    "docs/adr/0009-cross-language-corpus.md",
    "docs/adr/0010-dx-and-livebooks.md",
    "docs/adr/0011-public-contract-provenance.md",
    "docs/adr/0013-extension-profile-boundary.md",
    "docs/design/architecture.md",
    "docs/design/threat-model.md",
    "docs/guides/getting-started.md",
    "docs/guides/signing-and-verifying.md",
    "docs/guides/phoenix-and-plug.md",
    "docs/guides/req-and-finch.md",
    "docs/guides/ash.md",
    "docs/guides/ash-hooks.md",
    "docs/guides/web-bot-auth.md",
    "docs/guides/replay-protection.md",
    "docs/guides/key-discovery.md",
    "docs/guides/jose.md",
    "docs/guides/key-custody.md",
    "docs/guides/testing.md",
    "docs/operations/releases.md",
    "docs/reference/glossary.md",
    "docs/reference/standards.md",
    "livebooks/README.md",
    "livebooks/environment.livemd",
    "livebooks/rfc-ed25519.livemd"
  ]

  def project do
    # Dependency consumers do not load this library's config/config.exs.
    unless Code.ensure_loaded?(:json) do
      raise "RequestSeal requires Erlang/OTP 27 or newer for :json."
    end

    [
      app: :request_seal,
      version: "0.2.0",
      # Libraries declare a consumer range; development and notebook tools stay pinned.
      # OTP 27 supplies :json; both supported CI lanes are recorded in ADR 0008.
      elixir: "~> 1.18",
      name: "RequestSeal",
      source_url: "https://github.com/baselabs/request_seal",
      description:
        "Sign and verify HTTP requests and responses in Elixir: RFC 9421 HTTP Message Signatures, Web Bot Auth for AI agents, JWS/JWE, digests and replay protection, with Plug (including Phoenix), Req, Finch and Ash integrations.",
      elixirc_paths: elixirc_paths(Mix.env()),
      deps: dependencies(),
      aliases: ["hex.publish": [&release_runtime_check!/1, "hex.publish"]],
      package: [
        licenses: ["Apache-2.0", "BSD-3-Clause"],
        files: ["lib/**/*.ex", "mix.exs" | @public_documents],
        links: %{"GitHub" => "https://github.com/baselabs/request_seal"}
      ],
      docs: [
        main: "readme",
        extras:
          Enum.map(@public_documents, fn
            "livebooks/README.md" -> {"livebooks/README.md", filename: "livebooks"}
            path -> path
          end),
        groups_for_extras: [
          "Start here": ["README.md"],
          Guides: ~r/docs\/guides\//,
          Reference: ~r/docs\/reference\//,
          "Design and decisions": [~r/docs\/design\//, ~r/docs\/adr\//],
          Livebooks: ~r/livebooks\//,
          Contributing: [
            "CONTRIBUTING.md",
            "SECURITY.md",
            "docs/guides/getting-started.md",
            "docs/guides/testing.md",
            ~r/docs\/operations\//
          ]
        ],
        groups_for_modules: [
          Core: [
            RequestSeal,
            RequestSeal.Message,
            RequestSeal.Body,
            RequestSeal.FieldOccurrence,
            RequestSeal.TransportFacts,
            RequestSeal.Policy,
            RequestSeal.Verification,
            RequestSeal.Error,
            RequestSeal.SignatureBase,
            ~r/^RequestSeal\.(StructuredFields|Digest|Crypto|Quorum|AcceptSignature)(\.|$)/
          ],
          "Keys and custody": [
            RequestSeal.PublicKey,
            RequestSeal.KeyHandle,
            RequestSeal.KeyIdentity,
            ~r/^RequestSeal\.Custody(\.|$)/
          ],
          Discovery: ~r/^RequestSeal\.Discovery(\.|$)/,
          Replay: ~r/^RequestSeal\.Replay(\.|$)/,
          JOSE: ~r/^RequestSeal\.JOSE(\.|$)/,
          "Web Bot Auth": ~r/^RequestSeal\.WebBotAuth(\.|$)/,
          Integrations: ~r/^RequestSeal\.(Req|Finch|Plug|Ash|AshHooks|Adapter)(\.|$)/,
          Profiles: [RequestSeal.Profile]
        ]
      ]
    ]
  end

  defp release_runtime_check!(_args) do
    unless Version.match?(System.version(), ">= 1.20.0") do
      Mix.raise(
        "Publishing RequestSeal requires Elixir 1.20 or newer so optional integrations remain in Hex metadata."
      )
    end
  end

  defp dependencies do
    # Ash needs StreamData in dev/prod; scope the test override here to keep it out of Hex requirements.
    test_dependencies =
      if Mix.env() == :test,
        do: [{:stream_data, "~> 1.1", only: :test, override: true}],
        else: []

    [
      {:ex_doc, "~> 0.40.4", only: [:dev, :test], runtime: false},
      # Require the Ash security patch line used by the optional integrations.
      {:ash, "~> 3.34 and >= 3.34.3", optional: true},
      {:simple_sat, "~> 0.1", only: :test},
      # Use stable Req; 0.8.0-rc.0 is a release candidate.
      # Consumers own client startup; importing this library starts no pool.
      {:req, "~> 0.7.4", optional: true, runtime: false},
      {:finch, ">= 0.23.0 and < 0.25.0", optional: true, runtime: false},
      {:plug, "~> 1.20.3", optional: true, runtime: false},
      {:bandit, "~> 1.12.5", only: :test},
      {:phoenix, "~> 1.8.15", only: :test},
      {:postgrex, "~> 0.22.4", optional: true, runtime: false}
    ] ++ test_dependencies ++ owned_integrations()
  end

  # These optional packages require Elixir 1.20. Floor/mid developer and CI
  # toolchains omit them; the latest lane executes their real integrations.
  defp owned_integrations do
    if Version.match?(System.version(), ">= 1.20.0") do
      [
        {:ash_onetime, "~> 1.5", optional: true},
        {:ash_hooks, "~> 2.0", optional: true}
      ]
    else
      []
    end
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  def application, do: [extra_applications: [:crypto, :public_key]]
end