Canonical Repository Setup#

The canonical repository (github.com/<org>/apis) is the single source of truth for all organization API schemas. This guide walks through creating the repository, scaffolding the directory structure, and configuring GitHub protections β€” either automatically via --setup-github or manually.

Prerequisites#

Tool

Purpose

Install

apx

Scaffold & manage schemas

brew install infobloxopen/tap/apx or installation guide

gh

GitHub CLI (optional, required for --setup-github)

brew install gh or cli.github.com

git

Version control

Pre-installed on most systems

buf

Protocol Buffer tooling (optional)

brew install bufbuild/buf/buf

Tip

If you plan to use --setup-github for automated protection rules and org secrets, authenticate gh first:

gh auth login
gh auth refresh -h github.com -s admin:org   # needed for org secrets

Step 1: Create the GitHub Repository#

Create the canonical repo on GitHub before scaffolding:

# Option A: via gh CLI
gh repo create <org>/apis --public --clone
cd apis

# Option B: Create on github.com, then clone
git clone https://github.com/<org>/apis.git
cd apis

Note

The repo name apis is conventional but not required. Use whatever name fits your organization β€” just pass --repo=<name> to apx init canonical.

Step 2: Scaffold the Structure#

Interactive Mode (default)#

apx init canonical

APX auto-detects your org and repo from the git remote. If it can’t detect them, it prompts:

πŸš€ Initializing canonical API repository!

Organization name: [auto-detected or enter manually]
Repository name:   [auto-detected or enter manually]

Non-Interactive Mode#

apx init canonical --org=<org> --repo=apis --non-interactive

All flags:

Flag

Description

Default

--org

GitHub organization name

Auto-detected from git remote

--repo

Repository name

Auto-detected from current directory

--skip-git

Skip the β€œnext steps” git instructions

false

--non-interactive

Disable prompts; require all flags

false

--setup-github

Configure GitHub protections via gh CLI

false

--app-id

GitHub App ID (with --setup-github)

Cached or created via manifest flow

--app-pem-file

Path to GitHub App private key PEM (with --setup-github)

Cached or created via manifest flow

What Gets Generated#

apis/
β”œβ”€β”€ apx.yaml                     # APX project configuration
β”œβ”€β”€ buf.yaml                     # Buf v2 lint/breaking policy
β”œβ”€β”€ buf.work.yaml                # Buf workspace (all format directories)
β”œβ”€β”€ CODEOWNERS                   # Per-path team ownership
β”œβ”€β”€ README.md                    # Repo overview
β”œβ”€β”€ catalog/
β”‚  └── catalog.yaml              # API catalog (updated by CI)
β”œβ”€β”€ .github/
β”‚  └── workflows/
β”‚     β”œβ”€β”€ ci.yml                 # PR validation (lint + breaking)
β”‚     └── on-merge.yml           # Post-merge tagging + catalog update
β”œβ”€β”€ proto/
β”‚  └── .gitkeep
β”œβ”€β”€ openapi/
β”‚  └── .gitkeep
β”œβ”€β”€ avro/
β”‚  └── .gitkeep
β”œβ”€β”€ jsonschema/
β”‚  └── .gitkeep
└── parquet/
   └── .gitkeep

The command reports each generated file:

Initializing canonical API repository...
Organization: acme
Repository: apis

βœ“ Created directory structure
βœ“ Generated buf.yaml
βœ“ Generated CODEOWNERS
βœ“ Generated catalog.yaml
βœ“ Generated README.md
βœ“ Generated apx.yaml
βœ“ Generated .github/workflows/ci.yml
βœ“ Generated .github/workflows/on-merge.yml

βœ“ Canonical API repository initialized successfully!

Tip

Running apx init canonical is idempotent β€” it skips files that already exist, so you can safely re-run it.

Step 3: Configure GitHub Protections#

You have two options: automated (recommended) or manual.

Option A: Automated Setup (--setup-github)#

apx init canonical --org=acme --repo=apis --setup-github

This performs four operations via the GitHub API:

  1. GitHub App creation β€” If no App is cached, APX opens your browser to create one via the GitHub App manifest flow. The App is named apx-<repo>-<org> and granted contents:write, pull_requests:write, and metadata:read permissions.

  2. Org secrets β€” Sets two organization-level Actions secrets:

    • APX_APP_ID β€” The App’s numeric ID

    • APX_APP_PRIVATE_KEY β€” The App’s private key (PEM)

  3. Branch protection on main β€” Requires:

    • Pull request reviews (1 approval, CODEOWNERS review, dismiss stale reviews)

    • Status checks (validate must pass)

    • Strict status checks (branch must be up to date)

  4. Tag protection ruleset β€” Creates an apx-tag-protection ruleset that prevents direct tag creation/deletion, allowing only organization admins as bypass actors. This ensures CI is the sole creator of release tags.

What you see:

Opening browser to create GitHub App for org "acme"...
App created! App ID: 123456
Opening browser to install the App...
Waiting for installation to complete...
App installed on "acme"!

Configuring GitHub repository...
  βœ“ Created: org secret APX_APP_ID
  βœ“ Created: org secret APX_APP_PRIVATE_KEY
  βœ“ Created: branch protection on main
  βœ“ Created: tag protection ruleset

βœ“ Canonical API repository initialized successfully!

On subsequent runs, already-configured items are reported as skipped:

  βœ“ Already configured: org secret APX_APP_ID
  βœ“ Already configured: branch protection on main

Important

The --setup-github flow requires the admin:org OAuth scope. If your token is missing it, APX will tell you:

gh token is missing the 'admin:org' scope needed for org secrets.
Run: gh auth refresh -h github.com -s admin:org

Some operations (org secrets, tag rulesets) require organization admin privileges. If you lack them, APX logs a warning with the manual command to run as an admin.

Credential Caching#

APX caches GitHub App credentials locally at ~/.config/apx/:

File

Contents

<org>-app-id

GitHub App numeric ID

<org>-app-slug

GitHub App slug (e.g. apx-apis-acme)

<org>-app.pem

Private key (mode 0600)

On subsequent runs, APX uses cached credentials instead of re-creating the App. To supply credentials manually on a new machine:

apx init canonical --setup-github \
  --app-id=123456 \
  --app-pem-file=/path/to/private-key.pem

Option B: Manual Setup#

If you prefer to configure protections by hand (or lack org admin access), skip --setup-github and follow the printed instructions:

Next steps:
1. Initialize git: git init
2. Add files: git add .
3. Commit: git commit -m 'Initial canonical repository scaffold'
4. Create GitHub repository and set up branch protection:
   - Require pull request reviews
   - Require status checks (lint, breaking)
   - Require CODEOWNERS review
   - Restrict direct pushes to main
5. Push: git remote add origin <url> && git push -u origin main

Or re-run with --setup-github to configure automatically via gh CLI.

Manual Branch Protection#

Go to Settings β†’ Branches β†’ Add rule for main:

  • Require a pull request before merging (1 approval)

  • Require review from Code Owners

  • Dismiss stale pull request approvals

  • Require status checks to pass β†’ add validate

  • Require branches to be up to date before merging

Manual Tag Protection#

Go to Settings β†’ Rules β†’ Rulesets β†’ New tag ruleset:

  • Name: apx-tag-protection

  • Target: All tags

  • Rules: Restrict creations, Restrict deletions

  • Bypass: Organization admins only

This ensures only CI (via the GitHub App token) can create release tags.

Generated File Details#

buf.yaml#

Organization-wide lint and breaking change policy for Protocol Buffers:

version: v2
modules:
  - path: proto
breaking:
  use:
    - FILE
lint:
  use:
    - STANDARD

buf.work.yaml#

Buf workspace configuration aggregating all schema format directories:

version: v2
directories:
  - proto
  - openapi
  - avro
  - jsonschema
  - parquet

CODEOWNERS#

Per-path ownership rules. Customize team names after scaffolding:

* @<org>/api-owners

/proto/    @<org>/proto-owners
/openapi/  @<org>/openapi-owners
/avro/     @<org>/avro-owners
/jsonschema/ @<org>/jsonschema-owners
/parquet/  @<org>/parquet-owners

catalog/catalog.yaml#

Empty catalog seed, automatically updated by CI on merge:

version: 1
org: <org>
repo: apis
modules: []

.github/workflows/ci.yml#

Runs on every pull request to main. Installs APX, lints schemas, and checks for breaking changes:

name: APX Schema CI
on:
  pull_request:
    branches: [main]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: infobloxopen/apx@v1
      - run: apx lint
      - run: apx breaking --against origin/main

.github/workflows/on-merge.yml#

Runs on push to main. Uses the GitHub App token to validate, update the catalog, and commit changes:

name: APX On Merge
on:
  push:
    branches: [main]
jobs:
  tag-and-catalog:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ secrets.APX_APP_ID }}
          private-key: ${{ secrets.APX_APP_PRIVATE_KEY }}
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token }}
      - uses: infobloxopen/apx@v1
      - run: apx lint
      - run: apx catalog generate
      - run: |
          git config user.name "apx-publisher[bot]"
          git config user.email "apx-publisher[bot]@users.noreply.github.com"
          if git diff --quiet catalog/; then
            echo "No catalog changes"
          else
            git add catalog/
            git commit -m "chore: update catalog [skip ci]"
            git push
          fi

Verifying the Setup#

After pushing your scaffold and configuring protections:

# Confirm the scaffold is committed
git log --oneline -1
# β†’ abc1234 Initial canonical repository scaffold

# Verify branch protection (requires gh)
gh api repos/<org>/apis/branches/main/protection --jq '.required_pull_request_reviews'

# Verify tag ruleset
gh api repos/<org>/apis/rulesets --jq '.[].name'
# β†’ apx-tag-protection

# Verify org secrets exist
gh secret list --org <org> | grep APX_
# β†’ APX_APP_ID          Updated ...
# β†’ APX_APP_PRIVATE_KEY Updated ...

Next Steps#

Once the canonical repo is set up:

  1. Understand the directory structure β€” Learn how APIs are organized by format, domain, and version

  2. Set up CI templates β€” Customize validation and release workflows

  3. Configure branch & tag protection β€” Deeper dive into protection rules

  4. Set up an app repo β€” Start authoring schemas with apx init app

  5. Release your first API β€” Walk through the release submission flow