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 |
|
gh |
GitHub CLI (optional, required for |
|
git |
Version control |
Pre-installed on most systems |
buf |
Protocol Buffer tooling (optional) |
|
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 |
|---|---|---|
|
GitHub organization name |
Auto-detected from git remote |
|
Repository name |
Auto-detected from current directory |
|
Skip the βnext stepsβ git instructions |
|
|
Disable prompts; require all flags |
|
|
Configure GitHub protections via |
|
|
GitHub App ID (with |
Cached or created via manifest flow |
|
Path to GitHub App private key PEM (with |
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:
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 grantedcontents:write,pull_requests:write, andmetadata:readpermissions.Org secrets β Sets two organization-level Actions secrets:
APX_APP_IDβ The Appβs numeric IDAPX_APP_PRIVATE_KEYβ The Appβs private key (PEM)
Branch protection on
mainβ Requires:Pull request reviews (1 approval, CODEOWNERS review, dismiss stale reviews)
Status checks (
validatemust pass)Strict status checks (branch must be up to date)
Tag protection ruleset β Creates an
apx-tag-protectionruleset 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 |
|---|---|
|
GitHub App numeric ID |
|
GitHub App slug (e.g. |
|
Private key (mode |
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
validateRequire branches to be up to date before merging
Manual Tag Protection#
Go to Settings β Rules β Rulesets β New tag ruleset:
Name:
apx-tag-protectionTarget: 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:
Understand the directory structure β Learn how APIs are organized by format, domain, and version
Set up CI templates β Customize validation and release workflows
Configure branch & tag protection β Deeper dive into protection rules
Set up an app repo β Start authoring schemas with
apx init appRelease your first API β Walk through the release submission flow