apx is a CLI tool that implements the canonical repository pattern for API schema management. It enables organizations to centralize API schemas in a single source of truth while allowing teams to author schemas in their application repositories with canonical import paths.
- Canonical Import Paths: Single import path that works in development and production
- Custom Import Roots: Decouple public API identity from Git hosting — use
go.acme.dev/apiswhile hosting atgithub.com/acme/apis - go.work Overlays: Seamless transition between local development and released modules
- Organization-Wide Catalog: Centralized API discovery across all teams
- Multi-Format Support: Protocol Buffers, OpenAPI, Avro, JSON Schema, Parquet (see Format Maturity)
- Schema Validation: Automated linting and breaking change detection
- Code Generation: Generate client code for Go, Python, and Java
- Policy Enforcement: Org-wide lint and breaking change policies
APX implements a two-repository pattern:
- Canonical Repository (
github.com/<org>/apis): Single source of truth for all released APIs - App Repositories: Where teams author schemas and generate code with canonical import paths
Benefits:
- Import paths never change when switching from dev to production
- No
replacedirectives or import rewrites - Clean dependency management via
go.workoverlays - Optional
import_rootdecouples Go import paths from Git hosting (e.g.go.acme.dev/apis)
See the Quick Start Guide for a comprehensive walkthrough.
# Create your organization's canonical API repository
git clone https://github.com/<org>/apis.git
cd apis
# Initialize the canonical structure
apx init canonical --org=<org> --repo=apisCreates:
apis/
├── buf.yaml # Org-wide lint/breaking policy
├── buf.work.yaml # Workspace config
├── CODEOWNERS # Per-path ownership
├── catalog/
│ └── catalog.yaml # API discovery catalog
└── proto/ # Schema directories
└── openapi/
└── avro/
└── jsonschema/
└── parquet/
# In your application repository
cd /path/to/your-app
# Initialize app structure
apx init app --org=<org> --repo=<app-repo> internal/apis/proto/payments/ledger
# Lint your schema
apx lint internal/apis/proto/payments/ledger
# Check for breaking changes
apx breaking --against=HEAD^ internal/apis/proto/payments/ledger
# Release to canonical repo
apx release prepare proto/payments/ledger/v1 --version v1.0.0 --lifecycle stable
apx release submit# Search for APIs
apx search payment
# Add dependency
apx add proto/payments/ledger/v1@v1.2.3
# Generate code with canonical imports
apx gen go
# Your code now uses: github.com/<org>/apis/proto/payments/ledger/v1
# Works seamlessly via go.work overlay!
# When ready, switch to released module
apx unlink proto/payments/ledger/v1brew install --cask infobloxopen/tap/apxscoop bucket add infobloxopen https://github.com/infobloxopen/scoop-bucket
scoop install infobloxopen/apxcurl -fsSL https://raw.githubusercontent.com/infobloxopen/apx/main/install.sh | bashPin a specific version or change the install directory:
curl -fsSL https://raw.githubusercontent.com/infobloxopen/apx/main/install.sh | VERSION=1.2.3 bash
curl -fsSL https://raw.githubusercontent.com/infobloxopen/apx/main/install.sh | INSTALL_DIR=/usr/local/bin bashDownload the latest release from GitHub Releases for your platform.
go install github.com/infobloxopen/apx/cmd/apx@latestAPX integrates with format-specific tooling:
# Install all required tools
curl -sSL https://raw.githubusercontent.com/infobloxopen/apx/main/scripts/install-tools.sh | bashOr install individually:
- buf - Protocol Buffer linting and breaking change detection
- spectral - OpenAPI linting (optional)
- oasdiff - OpenAPI breaking changes (optional)
Bootstrap a canonical API repository.
apx init canonical --org=myorg --repo=apisFlags:
--org: Organization name (required)--repo: Repository name (required)--skip-git: Skip git initialization--non-interactive: Skip interactive prompts
Bootstrap an application repository for schema authoring.
apx init app internal/apis/proto/payments/ledgerFlags:
--org: Organization name (required)--non-interactive: Skip interactive prompts
Auto-detects format from path:
/proto/-> Protocol Buffers/openapi/-> OpenAPI/avro/-> Avro/jsonschema/-> JSON Schema/parquet/-> Parquet
Validate schema files for syntax and style issues.
apx lint # Lint current directory
apx lint internal/apis/proto/payments # Lint specific path
apx lint --format=proto # Explicit formatCheck for breaking changes.
apx breaking internal/apis/proto/payments
apx breaking --format=openapiAPX uses a structured multi-step release pipeline (prepare -> submit -> finalize -> promote)
with manifest tracking, policy checks, and immutable release records.
Structured release pipeline for CI and production workflows.
# Prepare a release (validate, build manifest)
apx release prepare proto/payments/ledger/v1 --version v1.0.0 --lifecycle stable
# Submit to canonical repo (opens PR)
apx release submit
# After PR merge, canonical CI runs finalize
apx release finalize
# Inspect current release state
apx release inspect proto/payments/ledger/v1
# List release history
apx release history proto/payments/ledger/v1
# Promote lifecycle (e.g. beta -> stable)
apx release promote proto/payments/ledger/v1 --to stable --version v1.0.0Search for APIs in the canonical catalog.
apx search # List all APIs
apx search payment # Search by keyword
apx search --format=proto # Filter by format
apx search --catalog=path/to/catalog.yamlAdd a schema dependency.
apx add proto/payments/ledger/v1@v1.2.3
apx add proto/users/profile/v1 # Uses latestUpdates both apx.yaml and apx.lock files.
Generate client code from dependencies.
apx gen go # Generate Go code
apx gen python # Generate Python code
apx gen java # Generate Java codeGenerated structure:
/internal/gen/
├── go/
│ └── proto/payments/ledger@v1.2.3/
├── python/
│ └── proto/payments/ledger/
└── java/
└── proto/payments/ledger/
Note: /internal/gen/ is git-ignored. Never commit generated code.
Synchronize go.work with active Go overlays.
apx syncRegenerates go.work to include all overlays in /internal/gen/go/.
Remove overlay and switch to released module.
apx unlink proto/payments/ledger/v1Removes overlay from /internal/gen/ and updates go.work.
Generated by apx init app:
# import_root: go.myorg.dev/apis # optional: custom Go import prefix
api:
id: proto/payments/ledger/v1
format: proto
domain: payments
name: ledger
line: v1
lifecycle: beta
source:
repo: github.com/myorg/apis
path: proto/payments/ledger/v1
releases:
current: v1.0.0-beta.1When import_root is set, Go module and import paths use the custom root instead of source.repo. For example, with import_root: go.myorg.dev/apis, the Go import path becomes go.myorg.dev/apis/proto/payments/ledger/v1.
Pinned dependency versions (generated by apx add):
dependencies:
proto/payments/ledger/v1:
repo: github.com/myorg/apis
ref: v1.2.3
modules:
- proto/payments/ledger/v1API discovery catalog (auto-generated):
version: 1
org: myorg
repo: apis
modules:
- name: proto/payments/ledger/v1
format: proto
description: Payment ledger API
version: v1.2.3
path: proto/payments/ledger/v1- Generate overlay:
apx gen gocreates/internal/gen/go/proto/payments/ledger@v1.2.3/ - go.work magic:
apx syncupdatesgo.workto map canonical path to local overlay - Your code imports:
import "github.com/myorg/apis/proto/payments/ledger/v1" - Go resolves: Via
go.work, imports resolve to your local overlay
- Remove overlay:
apx unlink proto/payments/ledger/v1 - Add released module:
go get github.com/myorg/apis/proto/payments/ledger/v1@v1.2.3 - Imports unchanged: Same
import "github.com/myorg/apis/proto/payments/ledger/v1" - Go resolves: From released module in
go.mod
No import rewrites. No replace directives. It just works.
Organizations can decouple their public Go import paths from Git hosting by setting import_root in apx.yaml:
import_root: go.acme.dev/apisAll derived Go paths then use the custom root:
Without import_root |
With import_root: go.acme.dev/apis |
|---|---|
github.com/acme/apis/proto/payments/ledger |
go.acme.dev/apis/proto/payments/ledger |
github.com/acme/apis/proto/payments/ledger/v1 |
go.acme.dev/apis/proto/payments/ledger/v1 |
The source repository remains unchanged — only the public import identity shifts. This lets organizations migrate Git hosts or use vanity domains without breaking consumer imports.
/internal/gen/
├── go/ # Go overlays
│ ├── proto/payments/ledger@v1.2.3/
│ └── proto/users/profile@v1.0.1/
├── python/ # Python packages
│ ├── proto/payments/ledger/
│ └── proto/users/profile/
└── java/ # Java packages
├── proto/payments/ledger/
└── proto/users/profile/
Why language subdirectories?
- Prevents conflicts when generating for multiple languages
- Each language has its own namespace and structure
- Overlay manager handles language-specific path resolution
--config <file>: Specify config file (default:apx.yaml)--verbose: Enable verbose output--quiet: Suppress output--json: Output in JSON format--no-color: Disable colored output
name: API Schema Workflow
on:
pull_request:
paths:
- 'internal/apis/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install APX
uses: infobloxopen/apx@main
- name: Lint Schemas
run: apx lint internal/apis
- name: Check Breaking Changes
run: apx breaking internal/apis
release:
if: github.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.CANONICAL_REPO_TOKEN }}
- name: Install APX
uses: infobloxopen/apx@main
- name: Release to Canonical Repo
run: |
apx release prepare proto/payments/ledger/v1 \
--version v1.0.0 --lifecycle stable
apx release submit
env:
GIT_SSH_COMMAND: "ssh -i ${{ secrets.DEPLOY_KEY }}"Full Documentation - Complete documentation hosted on GitHub Pages
- Quick Start Guide
- Tutorial - Complete walkthrough
- Canonical Repository Structure
- Dependency Management
- Initialization Guide
- Release Guide
- CLI Reference
- FAQ & Troubleshooting
- Canonical repository initialization
- App repository scaffolding
- Schema validation (lint, breaking)
- Code generation (Go, Python, Java)
- Overlay management with go.work
- API discovery and search
- Dependency management (apx.lock)
- Release workflow (PR-based canonical submission)
- Multi-language overlay structure
- Offline/air-gapped mode via
apx fetch - GitHub Enterprise Server support
- Performance instrumentation
- Enhanced error messages with actionable guidance
See CHANGELOG.md for detailed release notes.
0: Success1: General error2: Validation/lint errors3: Breaking changes detected4: Dependency not found5: Configuration error
make buildmake test # Unit tests
go test -run TestScript # Integration testscripts
go test ./tests/integration # Full integration testsThe E2E test suite validates the complete APX workflow using k3d (lightweight Kubernetes) with Gitea as a git hosting simulator and testscript for test orchestration.
# Install E2E dependencies (k3d, kubectl)
make install-e2e-deps
# Run E2E tests (creates k3d cluster, deploys Gitea, runs scenarios)
make test-e2e
# Clean up any leftover E2E resources
make clean-e2eWhat it tests: Canonical repo bootstrap -> schema release -> cross-repo dependencies -> breaking change detection -> git history preservation -- all against a real git server.
Requirements: Docker, ~2GB free memory, ~56 seconds runtime.
See tests/e2e/README.md for the full developer guide.
See CONTRIBUTING.md for development guidelines.
Licensed under the Apache License, Version 2.0. See LICENSE for details.
- Documentation - Full documentation on GitHub Pages
- Issues - Bug reports and feature requests
- Discussions - Questions and community support
