gitaiflow MCP Deployment
This document is the operational reference for releasing gitaiflow AOT binaries and the Claude Desktop MCPB extension.
On this page ▾
- 1. Purpose
- 2. Release Architecture
- 3. Release Artifacts
- 4. Release Signing
- 5. Signing Key Storage
- Signing key present
- Signing key absent
- Final flow
- Important property
- 7.1 Installer metadata
- 7.2 Installer binaries
- 7.3 Installer entrypoints
- 7.4 Generated manifest
- Commands
- Why this order matters
- Step 1 — Rotate the key
- Step 2 — Verify the key
- Step 3 — Rebuild AOT
- Step 4 — Rebuild MCPB
- Step 5 — Sign the AOT release
- Step 6 — Generate the manifest
- Step 7 — Synchronize to R2
- Keep
- Do not add unless needed
- Normal release
- Key-rotation release
- SHA256SUMS.sig exists but verification fails
- MCPB rejects a newly released AOT binary
- R2 sync reports signing was skipped
- latest.txt appears stale
- Build
- Build macOS arm64 MCPB
- Rotate signing key
- Verify signing key
- Sign a release
- Generate manifest
- Synchronize to R2
Deployment, signing, key rotation, build, and R2 synchronization guide for the gitaiflow MCP extension and AOT releases.
1. Purpose
It covers:
- AOT release builds
- MCPB builds
- Ed25519 release signing
- Embedded public-key trust
- Signing-key rotation
- Signature verification
- R2 synchronization
- Cache behavior for mutable release metadata
- Normal releases
- Releases involving signing-key rotation
- Local/test synchronization
- Recommended release safeguards
- Common failure modes and recovery
The release process is intentionally split into build, signing, manifest generation, and R2 synchronization steps so each stage can be inspected independently.
2. Release Architecture
gitaiflow has two related release artifacts:
-
AOT binary
- Built with the gitaiflow AOT build process.
- Published under
installers/AOT/<version>/. - Protected by
SHA256SUMSandSHA256SUMS.sig.
-
MCPB extension
- Built for Claude Desktop.
- Published under
installers/MCP/<version>/. - The MCP server contains the public key used to verify trusted AOT releases.
The trust relationship is:
Release Signing Key
│
Ed25519 private key
│
▼
SHA256SUMS.sig
│
│ verifies
▼
SHA256SUMS
│
│ hashes
▼
AOT binaries
▲
│
trusted by
│
│ embedded public key
│
┌──────┴──────┐
│ │
install.sh MCP binary.pyThe private signing key is never committed to the repository.
The public key is intentionally embedded in the release verification code:
scripts/install/install.shmcp_server/binary.py
Both must contain the same public key.
3. Release Artifacts
An AOT release has the following general structure:
installers/
└── AOT/
└── v1.1.3/
├── gitaiflow-darwin-arm64
├── gitaiflow-linux-amd64
├── gitaiflow-linux-arm64
├── gitaiflow-windows-amd64.exe
├── SHA256SUMS
└── SHA256SUMS.sigThe MCP release has a structure similar to:
installers/
└── MCP/
└── v1.1.3/
└── gitaiflow-mcp-1.1.3-darwin-arm64.mcpbThe current MCP build process produces the MCPB from the MCP server and associated packaged resources. The build output is validated as an MCPB before being written to the installer directory.
4. Release Signing
gitaiflow uses Ed25519 signing for release integrity.
The signing process produces:
SHA256SUMS
SHA256SUMS.sigSHA256SUMS contains SHA-256 hashes for the AOT binaries.
SHA256SUMS.sig is the Ed25519 signature over the checksum file.
The verification chain is therefore:
AOT binary
│
▼
SHA-256
│
▼
SHA256SUMS
│
▼
Ed25519 signature
│
▼
SHA256SUMS.sig
│
▼
Embedded trusted public keyA valid signature establishes that the checksum manifest was signed by the trusted release key. The checksum manifest then establishes the expected hash of each binary.
5. Signing Key Storage
The private signing key is kept outside the repository.
On the development machine, the current workflow uses macOS Keychain storage with:
Service:
gitaiflow-release-signing-key
Account:
$USERThe private key must not be committed to Git.
When a key is generated by the rotation script, the script writes the new private key outside the repository and explicitly requires the operator to move it into secure secret storage and remove the temporary copy.
The rotation script also records public-key/timestamp information in:
scripts/release/KEY_ROTATIONS.logNo secret material should be logged.
6. gitai_r2.sh Signing Decision Flow
The R2 synchronization script supports two operating modes.
Signing key present
When GITAIFLOW_SIGNING_KEY_FILE is available, gitai_r2.sh inspects AOT release directories.
For every release:
- If
SHA256SUMS.sigis absent:- sign the release
- verify the resulting signature
- If
SHA256SUMS.sigalready exists:- verify the existing signature
- do not automatically replace or re-sign it
- If verification fails:
- stop the release process
Signing key absent
Without a signing key, the script operates in local/test mode.
It warns that release signing was skipped.
This is useful for local development and testing, but an unsigned release must not be promoted as a production release.
Final flow
flowchart TD
A["gitai_r2.sh"]
A --> B{"Signing key present?"}
B -->|Yes| C["Inspect every AOT release"]
B -->|No| D["Local / test mode"]
C --> E{"SHA256SUMS.sig exists?"}
E -->|No| F["Sign release"]
F --> G["Verify signature"]
E -->|Yes| H["Verify existing signature"]
G --> I{"Signature valid?"}
H --> I
I -->|Yes| J["Sync to R2"]
I -->|No| X["STOP<br/>Invalid signature"]
D --> K["Warning:<br/>Release signing skipped"]
K --> JImportant property
An existing signature is verified, not blindly trusted merely because the file exists.
This avoids the following unsafe situation:
SHA256SUMS.sig exists
│
▼
assume valid
│
▼
upload releaseInstead:
SHA256SUMS.sig exists
│
▼
verify against SHA256SUMS + trusted key
│
├── valid ──► upload
│
└── invalid ─► stop7. R2 Synchronization
gitai_r2.sh separates R2 synchronization into several categories.
7.1 Installer metadata
The following files are treated as mutable release metadata:
SHA256SUMS
SHA256SUMS.sig
latest.txtThey are uploaded with:
Cache-Control: no-store, must-revalidateThis is intentional.
These files can change at the same URL, particularly:
latest.txt
SHA256SUMS
SHA256SUMS.sigCaching them aggressively could result in a client receiving an inconsistent combination of release metadata and binaries.
7.2 Installer binaries
AOT and MCP binaries are synchronized separately.
The mutable metadata files are excluded from the binary transfer.
The synchronization also excludes common local filesystem artifacts such as:
.DS_Store
Thumbs.db
desktop.ini7.3 Installer entrypoints
The R2 sync includes the installer entrypoints:
install.sh
install-mcp.sh
install.ps1
install-mcp.ps17.4 Generated manifest
The gitaiflow manifest is synchronized separately after the installer artifacts.
The general R2 flow is:
flowchart LR
A["Local release artifacts"]
A --> B["Release metadata<br/>SHA256SUMS<br/>SHA256SUMS.sig<br/>latest.txt"]
A --> C["Installer binaries"]
A --> D["Installer entrypoints"]
A --> E["Generated manifest"]
B --> F["R2"]
C --> F
D --> F
E --> F8. Why Metadata Uses no-store
The release system overwrites stable URLs such as:
latest.txt
SHA256SUMS
SHA256SUMS.sigA stale edge cache can create a dangerous mismatch:
latest.txt
│
▼
v1.1.3
│
▼
SHA256SUMS from v1.1.2 ← stale cacheor:
SHA256SUMS
│
▼
new release
SHA256SUMS.sig
│
▼
old release signatureUsing:
Cache-Control: no-store, must-revalidatefor mutable metadata reduces this class of cache inconsistency.
Release binaries can remain cacheable because versioned binary paths are effectively immutable:
installers/AOT/v1.1.3/gitaiflow-darwin-arm64A future release should use a new versioned path:
installers/AOT/v1.1.4/gitaiflow-darwin-arm649. Normal Release Flow
A normal release does not rotate the signing key.
The recommended high-level order is:
Build AOT
│
▼
Build MCPB
│
▼
Sign AOT release
│
▼
Generate manifest
│
▼
Verify / sign through R2 sync
│
▼
Sync to R2Commands
# Build the AOT release
make build
# Build the MCPB for macOS arm64
make mcp-build-mac-arm64
# Generate the gitaiflow R2 manifest
python3 scripts/r2/generate_gitai_manifest.py
# Synchronize the release to R2
./scripts/r2/gitai_r2.shIf signing is being performed explicitly before synchronization:
./scripts/release/sign_release.sh v1.1.3
python3 scripts/r2/generate_gitai_manifest.py
./scripts/r2/gitai_r2.shThe explicit signing step is useful when the release operator wants the signature generation to be visible and independently verifiable before the R2 synchronization.
When GITAIFLOW_SIGNING_KEY_FILE is configured, gitai_r2.sh can also sign an AOT release when its signature is missing.
10. Key Verification Before Release
Before signing a release, verify that the signing key and embedded public key agree.
./scripts/release/verify_release_signing_key.shThe expected relationship is:
Private key
│
▼
derive public key
│
▼
compare
│
├── matches ──► release can be signed
│
└── mismatch ─► stopA successful verification should show that the derived key and embedded binary.py public key match.
The same trust material is also embedded in install.sh.
11. Signing an AOT Release Manually
To explicitly sign a release:
./scripts/release/sign_release.sh v1.1.3This generates:
installers/AOT/v1.1.3/SHA256SUMS
installers/AOT/v1.1.3/SHA256SUMS.sigThe checksum manifest covers the AOT binaries, for example:
gitaiflow-darwin-arm64
gitaiflow-linux-amd64
gitaiflow-linux-arm64
gitaiflow-windows-amd64.exeBoth files must be available to the R2 synchronization process.
12. Signing Through gitai_r2.sh
The R2 script supports:
GITAIFLOW_SIGNING_KEY_FILEWhen this is set, the script can sign AOT releases that do not already have signatures.
Conceptually:
export GITAIFLOW_SIGNING_KEY_FILE="/secure/path/release-signing.key.b64"
./scripts/r2/gitai_r2.shThe script should not automatically re-sign an existing release merely because a signing key is available.
That is important for release reproducibility.
A release already signed by the trusted release key should remain signed by that key.
13. Signing-Key Rotation
Signing-key rotation is a special release operation.
The most important rule is:
The MCPB must be rebuilt after the public key is rotated and before an AOT release is signed with the new private key.
The correct order is:
ROTATE SIGNING KEY
│
▼
VERIFY EMBEDDED PUBLIC KEY
│
▼
REBUILD AOT + MCPB
│
▼
SIGN AOT RELEASE
│
▼
GENERATE MANIFEST
│
▼
SYNC TO R2Why this order matters
The MCP server contains the trusted public key.
If the MCPB was built before key rotation:
Old MCPB
│
└── trusts OLD public key
New AOT release
│
└── signed with NEW private keyThe old MCPB may reject the new AOT release.
Therefore:
New signing key
│
├── new install.sh trust
│
├── new mcp_server/binary.py trust
│
└── new AOT signaturesmust be released consistently.
14. Key-Rotation Commands
Step 1 — Rotate the key
./scripts/release/rotate_signing_key.shThe rotation script:
- generates a new Ed25519 keypair
- updates the embedded public key in
install.sh - updates the embedded public key in
mcp_server/binary.py - verifies that both files contain the same public key
- writes the new private key outside the repository
- records public-key/timestamp information in
KEY_ROTATIONS.log
The script itself requires the operator to move the new private key into secure secret storage and delete the temporary local copy.
Step 2 — Verify the key
./scripts/release/verify_release_signing_key.shDo not proceed if the derived key and embedded public key do not match.
Step 3 — Rebuild AOT
make buildStep 4 — Rebuild MCPB
make mcp-build-mac-arm64This step is mandatory after rotation because the MCPB must contain the newly trusted public key.
Step 5 — Sign the AOT release
./scripts/release/sign_release.sh v1.1.3Step 6 — Generate the manifest
python3 scripts/r2/generate_gitai_manifest.pyStep 7 — Synchronize to R2
./scripts/r2/gitai_r2.sh15. Complete Key-Rotation Release
The complete sequence can therefore be run as:
# Rotate
./scripts/release/rotate_signing_key.sh
# Verify new public key is correctly embedded
./scripts/release/verify_release_signing_key.sh
# Rebuild AOT
make build
# Rebuild MCPB AFTER rotation
make mcp-build-mac-arm64
# Sign release using the new private key
./scripts/release/sign_release.sh v1.1.3
# Generate R2 manifest
python3 scripts/r2/generate_gitai_manifest.py
# Sync to R2
./scripts/r2/gitai_r2.sh16. Key-Rotation Safety Rule
Never perform this sequence:
Build MCPB
│
▼
Rotate signing key
│
▼
Sign AOT with new key
│
▼
PublishThat creates a potentially incompatible release:
MCPB
└── old public key
AOT
└── new signatureInstead:
Rotate
│
▼
Update trusted public key
│
▼
Rebuild MCPB
│
▼
Sign AOT
│
▼
Publish both17. Existing Installed Versions After Rotation
Rotating the signing key does not invalidate an already-installed AOT executable merely because the key changed.
The existing binary can continue to run.
The compatibility concern is the verification path used when obtaining or upgrading to a newly signed release.
In particular, an older MCPB containing only the old public key may not accept a newly signed AOT release.
This is why the new MCPB must be released as part of the key-rotation transition.
18. Future Key Rotation Strategy
The current release does not require dual-key verification.
For a future rotation where a large population of older MCPB installations must be supported, a migration strategy can be considered:
Phase 1
───────
MCP trusts OLD + NEW
↓
Phase 2
───────
Release new MCPB
↓
Phase 3
───────
Sign new AOT releases with NEW
↓
Phase 4
───────
Allow migration window
↓
Phase 5
───────
Remove OLD trustThis is a future migration strategy, not a requirement for the current release process.
Do not introduce dual-key complexity unless an actual key rotation requires it.
19. Local / Test R2 Synchronization
The R2 script deliberately supports local/test usage without a signing key.
If:
GITAIFLOW_SIGNING_KEY_FILEis not configured, the script warns that signing is skipped.
This is useful when testing:
- R2 paths
- installer synchronization
- manifest generation
- cache behavior
- MCP distribution
- local release workflows
The warning should be treated as a production safety boundary.
No signing key
│
▼
Local / test mode
│
▼
Warning
│
▼
Sync may proceedDo not treat an unsigned synchronization as a production release.
20. Recommended Release Policy
The current architecture should remain intentionally simple.
Keep
- Ed25519 release signing
- public key embedded in
install.sh - public key embedded in
mcp_server/binary.py - explicit key verification
SHA256SUMSSHA256SUMS.sig- optional signing through
GITAIFLOW_SIGNING_KEY_FILE - existing-signature verification
- no automatic re-signing of valid existing releases
no-storemetadata headers- versioned immutable binary paths
- separate AOT and MCPB build steps
- explicit MCPB rebuild after key rotation
Do not add unless needed
- mandatory signing for every local R2 sync
- automatic re-signing after key rotation
- dual-key verification without a migration requirement
- unnecessary changes to the existing Makefile release workflow
The Makefile remains the preferred high-level entry point for builds.
The individual scripts remain available when an operator needs to inspect or repeat a specific release stage.
21. Recommended Release Checklist
Normal release
- Update version
- Build AOT
- Build MCPB
- Verify release signing key
- Sign AOT release
- Confirm
SHA256SUMSexists - Confirm
SHA256SUMS.sigexists - Verify signature
- Generate R2 manifest
- Run
gitai_r2.sh - Confirm R2 metadata is current
- Confirm versioned binaries are present
- Test installer
- Test MCPB in Claude Desktop
Key-rotation release
- Rotate signing key
- Secure the new private key
- Remove temporary private-key material
- Verify embedded public key
- Rebuild AOT
- Rebuild MCPB after rotation
- Sign AOT with the new key
- Verify signature
- Generate manifest
- Sync to R2
- Test installer verification
- Test MCPB AOT verification
- Test MCP tools from Claude Desktop
22. Troubleshooting
SHA256SUMS.sig exists but verification fails
Do not overwrite it automatically.
Stop the release and determine why the signature does not match.
Possible causes include:
- wrong signing key
- modified
SHA256SUMS - stale signature
- release artifacts changed after signing
- embedded public key mismatch
Correct sequence:
Invalid signature
│
▼
STOP
│
▼
Inspect release
│
▼
Determine cause
│
▼
Regenerate/re-sign intentionallyMCPB rejects a newly released AOT binary
First check whether the signing key was rotated.
If yes, verify:
./scripts/release/verify_release_signing_key.shThen confirm that the MCPB was rebuilt after the rotation.
The common incorrect sequence is:
MCPB built
↓
key rotated
↓
AOT signed
↓
MCPB not rebuiltThe correct sequence is:
key rotated
↓
MCPB rebuilt
↓
AOT signed
↓
publishR2 sync reports signing was skipped
A warning similar to:
Warning: GITAIFLOW_SIGNING_KEY_FILE not set -- skipping release signing.means the R2 script did not have access to the private signing key.
It does not necessarily mean the release has no signature.
If sign_release.sh was already run and produced:
SHA256SUMS.sigthe R2 script can upload that existing signature.
For production releases, verify the signature independently before publishing.
latest.txt appears stale
Remember that latest.txt is mutable release metadata.
The R2 synchronization intentionally uploads it with:
Cache-Control: no-store, must-revalidateVerify:
- the local latest version
- the generated/uploaded
latest.txt - the R2 object
- the release directory
- the checksum/signature metadata
Avoid treating cached metadata as proof that the local release was not uploaded.
23. Operational Command Reference
Build
make buildBuild macOS arm64 MCPB
make mcp-build-mac-arm64Rotate signing key
./scripts/release/rotate_signing_key.shVerify signing key
./scripts/release/verify_release_signing_key.shSign a release
./scripts/release/sign_release.sh v1.1.3Generate manifest
python3 scripts/r2/generate_gitai_manifest.pySynchronize to R2
./scripts/r2/gitai_r2.sh24. Normal Release Diagram
flowchart TD
A["Source changes"]
A --> B["Build AOT"]
B --> C["Build MCPB"]
C --> D["Verify release signing key"]
D --> E["Sign AOT release"]
E --> F["SHA256SUMS"]
E --> G["SHA256SUMS.sig"]
F --> H["Generate R2 manifest"]
G --> H
C --> H
H --> I["gitai_r2.sh"]
I --> J["Sync metadata"]
I --> K["Sync binaries"]
I --> L["Sync installer entrypoints"]
I --> M["Sync manifest"]
J --> N["R2"]
K --> N
L --> N
M --> N25. Key-Rotation Release Diagram
flowchart TD
A["Current signing key"]
A --> B["rotate_signing_key.sh"]
B --> C["Generate new Ed25519 key"]
C --> D["Update install.sh public key"]
C --> E["Update mcp_server/binary.py public key"]
D --> F["verify_release_signing_key.sh"]
E --> F
F --> G{"Public keys match?"}
G -->|No| X["STOP"]
G -->|Yes| H["Secure new private key"]
H --> I["Rebuild AOT"]
H --> J["Rebuild MCPB"]
I --> K["Sign AOT release"]
J --> L["MCPB trusts new public key"]
K --> M["Generate manifest"]
L --> M
M --> N["gitai_r2.sh"]
N --> O["Sync to R2"]26. End-to-End Deployment Model
flowchart LR
subgraph Build["Build"]
A["Source"]
B["AOT binary"]
C["MCPB"]
A --> B
A --> C
end
subgraph Trust["Release Trust"]
D["Ed25519 private key"]
E["SHA256SUMS"]
F["SHA256SUMS.sig"]
G["Embedded public key"]
B --> E
D --> F
E --> F
G --> F
end
subgraph Publish["R2"]
H["latest.txt"]
I["Versioned binaries"]
J["Installer entrypoints"]
K["gitaiflow manifest"]
end
B --> I
C --> I
F --> H
E --> H
B --> H
H --> L["gitai_r2.sh"]
I --> L
J --> L
K --> L
L --> M["Cloudflare R2"]27. Final Release Principle
The release system follows one simple trust model:
BUILD
↓
HASH
↓
SIGN
↓
VERIFY
↓
PUBLISHAnd for key rotation:
ROTATE
↓
UPDATE TRUST
↓
REBUILD MCPB
↓
REBUILD AOT
↓
SIGN
↓
VERIFY
↓
PUBLISHThe most important invariant is:
The public key embedded in the released verification code must match the private key used to sign the AOT release.
For the MCP release specifically:
After rotating the signing key, rebuild the MCPB before signing and publishing the AOT release with the new key.
This keeps the AOT installer, MCP server, release signatures, and R2 artifacts aligned.