Architecture

June 28, 2026 · View on GitHub

This document describes the system architecture from a security perspective. For detailed client interaction flows including authentication, verification, and caching, see Client Flows.

Ecosystem Inventory

Services

NameDescriptionProductionStagingRepository
Hex RegistryMain registry, web UI & APIhex.pmstaging.hex.pmhexpm/hexpm
Hex OperationsTerraform, Config, Fastly Compute--hexpm/hexpm-ops (private)
Hex DocsDocumentation hostinghexdocs.pmstaging.hexdocs.pmhexpm/hexdocs, hexpm/hexdocs-search
Hex PreviewPackage previewpreview.hex.pmpreview.staging.hex.pmhexpm/preview
Hex DiffPackage diff viewerdiff.hex.pmdiff.staging.hex.pmhexpm/diff

Out of scope: billing.hex.pm is excluded from this security documentation.

Client Libraries

NameDescriptionRepository
HexElixir Hex clienthexpm/hex
Hex CoreCore library for Elixir/Erlang clientshexpm/hex_core
Hex SolverVersion constraint resolverhexpm/hex_solver
hexpm-rustRust Hex client (used by Gleam)gleam-lang/hexpm-rust

Build Tools

NameDescriptionRepository
MixElixir build toolelixir-lang/elixir (lib/mix)
Rebar3Erlang build toolerlang/rebar3
erlang.mkErlang build toolninenines/erlang.mk
GleamGleam language & build toolgleam-lang/gleam

System Context

Shows the Hex ecosystem and its relationships with users and external systems.

C4Context
    title System Context Diagram for Hex Package Manager

    Person(consumer, "Package Consumer", "Developers and CI/CD systems using mix, rebar3, or gleam")
    Person(maintainer, "Package Maintainer", "Publishes and manages packages")
    Person(operator, "Hex.pm Operator", "Maintains registry infrastructure")

    System(hex, "Hex.pm", "Package registry API and web UI")
    System_Ext(fastly, "Fastly CDN", "Global content delivery and edge compute")
    System_Ext(storage, "Cloud Storage", "Package artifacts and registry files")

    System_Ext(github, "GitHub", "OAuth authentication and source code hosting")
    System_Ext(email, "Email Service", "Sends notifications and password resets")

    Rel(consumer, hex, "API requests", "HTTPS")
    Rel(consumer, fastly, "Repository requests", "HTTPS")
    Rel(maintainer, hex, "Publishes packages", "HTTPS + API Key")
    Rel(operator, hex, "Administers", "Internal")

    Rel(fastly, storage, "Serves registry + tarballs", "HTTPS cached")

    Rel(hex, github, "OAuth authentication")
    Rel(hex, email, "Sends notifications")
    Rel(hex, storage, "Stores packages")

    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")

Actors (see Actors for details):

  • Package Consumers - Developers and CI/CD systems that fetch packages
  • Package Maintainers - Publish and manage packages via the web UI or CLI
  • Organization Administrators - Manage private repositories and teams
  • Hex.pm Operators - Maintain and operate the registry infrastructure

External Systems:

  • GitHub provides OAuth authentication and hosts package source code
  • Email Service sends password resets, ownership notifications, etc.
  • Fastly CDN caches and delivers registry files and package tarballs globally

Container Diagram

Shows the internal services and data stores within the Hex ecosystem.

C4Container
    title Container Diagram for Hex Package Manager

    Person(consumer, "Package Consumer", "Uses mix, rebar3, or gleam")
    Person(maintainer, "Package Maintainer", "Publishes packages")

    System_Boundary(hex, "Hex Ecosystem") {
        Container(fastly, "Fastly CDN", "Compute@Edge", "Caches content, routes requests, runs edge logic")

        Container(hexpm_web, "hex.pm", "Phoenix/Elixir", "Web UI, HTTP API, package management")
        Container(hexdocs, "hexdocs.pm", "Phoenix/Elixir", "Documentation hosting and search")
        Container(preview, "preview.hex.pm", "Phoenix/Elixir", "Package preview before publishing")
        Container(diff, "diff.hex.pm", "Phoenix/Elixir", "Visual diff between package versions")

        ContainerDb(postgres, "PostgreSQL", "Database", "Users, packages, releases, API keys, organizations")
        ContainerDb(aws_s3, "Registry Storage", "AWS S3", "Tarballs and registry files")
        ContainerDb(gcs, "Service Storage", "Google Cloud Storage", "Documentation, diff cache, preview cache")
        Container(sqs_preview, "Preview Event Queue", "AWS SQS", "S3 ObjectCreated/Removed notifications consumed by preview")
        Container(sqs_docs, "Docs Event Queue", "AWS SQS", "S3 ObjectCreated/Removed notifications consumed by hexdocs")

        Container(registry_worker, "Registry Builder", "Elixir Worker", "Rebuilds registry after publishes")
        Container(notification_worker, "Notification Worker", "Elixir Worker", "Sends emails and webhooks")
    }

    System_Ext(email, "Email Service", "Sends notifications")

    Rel(consumer, fastly, "Fetches packages", "HTTPS")
    Rel(maintainer, fastly, "Publishes packages", "HTTPS")

    Rel(fastly, aws_s3, "Registry files", "/names, /versions, /packages/*")
    Rel(fastly, aws_s3, "Tarballs", "/tarballs/*")
    Rel(fastly, hexpm_web, "API requests", "/api/*")
    Rel(fastly, gcs, "Documentation, preview", "HTTPS")

    Rel(hexpm_web, postgres, "Reads/writes", "PostgreSQL")
    Rel(hexpm_web, aws_s3, "Stores packages", "S3 API")
    Rel(hexpm_web, registry_worker, "Triggers rebuild")
    Rel(hexpm_web, notification_worker, "Queues notifications")

    Rel(aws_s3, sqs_preview, "ObjectCreated/Removed events")
    Rel(aws_s3, sqs_docs, "ObjectCreated/Removed events")
    Rel(hexdocs, sqs_docs, "Consumes tarball/docs events", "SQS")
    Rel(hexdocs, aws_s3, "Reads tarballs", "S3 API")
    Rel(hexdocs, gcs, "Writes rendered docs", "GCS API")
    Rel(preview, sqs_preview, "Consumes tarball events", "SQS")
    Rel(preview, aws_s3, "Reads tarballs", "S3 API")
    Rel(preview, gcs, "Writes extracted previews", "GCS API")
    Rel(diff, fastly, "Fetches tarballs", "repo.hex.pm")
    Rel(diff, gcs, "Caches diffs", "GCS API")

    Rel(registry_worker, aws_s3, "Writes registry", "S3 API")
    Rel(notification_worker, email, "Sends email", "SendGrid HTTP API")

    UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="1")

Deployment Diagram

Shows the production infrastructure deployment topology.

C4Deployment
    title Deployment Diagram for Hex Package Manager

    Deployment_Node(internet, "Internet", "") {
        Deployment_Node(client_node, "Client Machine", "") {
            Container(client, "Build Tool", "mix/rebar3/gleam", "Fetches packages and registry")
        }
        Deployment_Node(browser_node, "Browser", "") {
            Container(browser, "Web Browser", "", "Access web UI")
        }
    }

    Deployment_Node(fastly_edge, "Fastly Edge", "Global POPs") {
        Container(edge, "Edge Cache + Compute@Edge", "Fastly", "Caches registry and tarballs, routes requests")
    }

    Deployment_Node(production, "Production Environment", "") {
        Deployment_Node(web_tier, "Web Tier", "") {
            Container(hex_prod, "hex.pm", "Phoenix/Elixir", "Main application")
            Container(hexdocs_prod, "hexdocs.pm", "Phoenix/Elixir", "Documentation")
            Container(preview_prod, "preview.hex.pm", "Phoenix/Elixir", "Preview")
            Container(diff_prod, "diff.hex.pm", "Phoenix/Elixir", "Diff viewer")
        }
        Deployment_Node(data_tier, "Data Tier", "") {
            ContainerDb(pg_prod, "PostgreSQL", "Cloud SQL", "Application data")
            ContainerDb(s3_prod, "Registry Bucket", "AWS S3", "Tarballs and registry files")
            ContainerDb(gcs_prod, "Service Buckets", "Google Cloud Storage", "Docs, diff cache, preview cache")
            Container(sqs_preview_prod, "Preview Event Queue", "AWS SQS", "Tarball events for preview")
            Container(sqs_docs_prod, "Docs Event Queue", "AWS SQS", "Tarball/docs events for hexdocs")
        }
    }

    Deployment_Node(mirrors, "Mirror Infrastructure", "") {
        Container(trusted_mirror, "Trusted Mirror", "Org-controlled", "Full authentication support")
        Container(untrusted_mirror, "Untrusted Mirror", "Community", "Public packages only - NO credentials")
    }

    Rel(client, edge, "HTTPS")
    Rel(browser, edge, "HTTPS")

    Rel(edge, hex_prod, "Cache MISS", "/api/*")
    Rel(edge, s3_prod, "Cache MISS", "Registry + tarballs")

    Rel(hex_prod, pg_prod, "PostgreSQL")
    Rel(hex_prod, s3_prod, "S3 API")

    Rel(s3_prod, sqs_preview_prod, "ObjectCreated/Removed events")
    Rel(s3_prod, sqs_docs_prod, "ObjectCreated/Removed events")
    Rel(hexdocs_prod, sqs_docs_prod, "Consumes events", "SQS")
    Rel(hexdocs_prod, s3_prod, "Reads tarballs", "S3 API")
    Rel(hexdocs_prod, gcs_prod, "Writes rendered docs", "GCS API")
    Rel(diff_prod, edge, "Fetch tarballs", "repo.hex.pm")
    Rel(diff_prod, gcs_prod, "Diff cache", "GCS API")
    Rel(preview_prod, sqs_preview_prod, "Consumes events", "SQS")
    Rel(preview_prod, s3_prod, "Reads tarballs", "S3 API")
    Rel(preview_prod, gcs_prod, "Writes previews", "GCS API")

    Rel(client, trusted_mirror, "Alternative path", "With auth")
    Rel(client, untrusted_mirror, "Alternative path", "NO auth - public only")

    Rel(trusted_mirror, edge, "Proxies via CDN")
    Rel(untrusted_mirror, edge, "Public packages only")

Deployment Security Notes

  • CDN Strategy: All traffic flows through Fastly; registry files and tarballs are heavily cached
  • Mirror Trust: Trusted mirrors can proxy authenticated requests; untrusted mirrors only serve public packages
  • High Availability: S3 provides artifact durability; Fastly provides global redundancy

Key Components

ComponentDescriptionSecurity Role
hex.pm APIPackage registry APIAuthentication, authorization, publishing
hex.pm WebWeb interfaceUser management, 2FA, session handling
Fastly CDN + AWS S3Content delivery and registry/tarball storageArtifact integrity, signed registry
Google Cloud StorageDocumentation, diff cache, preview cacheContent isolation
hexdocs.pmDocumentation hostingContent isolation, XSS prevention
Hex clientsBuild tool integrations (mix, rebar3, gleam)Signature verification, checksum validation
Registry BuilderBackground workerSigns registry files after publish
Notification WorkerBackground workerSends security-relevant notifications

Trust Boundaries

C4Context
    title Trust Boundary Diagram for Hex Package Manager

    Boundary(trusted, "Trusted Zone") {
        System(hexpm, "hex.pm", "Main application")
        System(fastly, "Fastly CDN", "Edge delivery")
        SystemDb(s3, "AWS S3", "Registry + tarballs")
        SystemDb(gcs, "Google Cloud Storage", "Docs, diff cache, preview cache")
        SystemDb(pg, "PostgreSQL", "Application data")
    }

    Boundary(semi_trusted, "Semi-Trusted Zone") {
        System(trusted_mirror, "Trusted Mirrors", "Organization-controlled mirrors")
    }

    Boundary(untrusted, "Untrusted Zone") {
        Person(client, "Client", "Build tools: mix, rebar3, gleam")
        System(untrusted_mirror, "Untrusted Mirrors", "Community mirrors")
    }

    Rel(client, hexpm, "API requests", "HTTPS + API Key")
    Rel(client, fastly, "Repository requests", "HTTPS + API Key")
    Rel(client, trusted_mirror, "Credentials OK", "HTTPS + API Key")
    Rel(client, untrusted_mirror, "NO CREDENTIALS", "HTTPS only")

    Rel(fastly, hexpm, "Internal")
    Rel(trusted_mirror, fastly, "Proxies requests")
    Rel(untrusted_mirror, fastly, "Public packages only")

    UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="3")

Boundary 1: Client to Registry API

  • Crosses: User credentials, API tokens, OAuth tokens
  • Controls: TLS, token scoping, 2FA for write operations

Boundary 2: Client to Repository (CDN)

  • Crosses: Package artifacts, registry data, API tokens, OAuth tokens (private packages)
  • Controls: Signed registry, checksums, signature verification
  • Critical: Clients must NEVER send credentials to untrusted mirrors

Boundary 3: Internal Services

  • Crosses: Database connections, internal APIs
  • Controls: Network isolation, access control

Boundary 4: Browser to Documentation

  • Crosses: User-generated documentation content
  • Controls: Per-package origin isolation (<package>.hexdocs.pm, <org>.hexorgs.pm/<package>), CSP headers

Communication Protocols

PathProtocolFormatAuthenticationIntegrity
Client → Registry filesHTTPSProtobuf + gzipNone (public) or API key (private) or OAuth2 (private)RSA-PKCS1-SHA512 signatures
Client → TarballsHTTPStarNone (public) or API key (private) or OAuth2 (private)Checksums
Client → APIHTTPSJSONAPI key (Bearer token) or OAuthTLS
Browser → WebHTTPSHTMLSession cookieTLS
Browser → DocsHTTPSHTMLNoneTLS + CSP
hex.pm → PostgreSQLTCPPostgreSQL protocolConnection credentials-
hex.pm → S3HTTPSS3 APIIAM credentialsTLS

Data Flow Diagrams

Publishing Flow

sequenceDiagram
    participant Client as Client (mix/rebar3/gleam)
    participant API as hex.pm API
    participant DB as PostgreSQL
    participant S3 as S3 Storage
    participant Worker as Registry Builder
    participant CDN as Fastly CDN

    Client->>API: Publish package (Bearer: API key or OAuth access token; OAuth tokens additionally require x-hex-otp)
    API->>API: Validate authentication & authorization
    API->>DB: Store package metadata
    API->>S3: Upload tarball
    API->>Worker: Trigger registry rebuild
    Worker->>DB: Read package data
    Worker->>Worker: Build & sign registry files
    Worker->>S3: Upload signed registry
    API->>CDN: Cache invalidation
    API-->>Client: Success response

Installation Flow

sequenceDiagram
    participant Client as Client (mix/rebar3/gleam)
    participant CDN as Fastly CDN
    participant S3 as S3 Storage

    Client->>CDN: Fetch registry (/names, /versions)
    CDN->>S3: Cache miss (if needed)
    S3-->>CDN: Registry files
    CDN-->>Client: Signed registry
    Client->>Client: Verify RSA-PKCS1-SHA512 signature
    Client->>CDN: Fetch package tarball
    CDN->>S3: Cache miss (if needed)
    S3-->>CDN: Tarball
    CDN-->>Client: Package tarball
    Client->>Client: Verify checksum
    Client->>Client: Extract contents