Shell 8.6%
Other 91.4%
26 1 0

Clone this repository

https://tangled.org/gdiazlo.tngl.sh/repo
git@tangled.org:gdiazlo.tngl.sh/repo

For self-hosted knots, clone URLs may differ based on your setup.

README.md

Private OPAM Repository#

A private OPAM repository with tooling for publishing OCaml packages.

Repository Structure#

├── bin/opam-publish     # CLI tool for publishing packages
├── packages/            # Published package metadata
│   └── <pkg>/
│       └── <pkg>.<version>/
│           └── opam
└── repo                 # OPAM repository marker

Architecture#

┌──────────────┐   opam-publish   ┌─────────────┐
│ Your Package │ ────────────────▶│ Backblaze   │◀── tarballs
└──────────────┘                  │     B2      │
                                  └─────────────┘
                                         │
                                         ▼
                                  ┌─────────────┐
                                  │ Cloudflare  │◀── CDN + custom domain
                                  └─────────────┘
                                         
┌──────────────┐   opam-publish   ┌─────────────┐
│ Your Package │ ────────────────▶│ This Repo   │◀── metadata only
└──────────────┘                  └─────────────┘

Setup#

Requirements#

  • uv (for running b2 CLI tool)
  • git (for pushing to this repo)
  • tar, sha256sum (standard Unix tools)
  • opam (optional, for linting validation)
  • SSH access to this repository

Configuration#

1. Authorize b2 CLI:

uv tool run b2 account authorize <applicationKeyId> <applicationKey>

You can get your application key from the Backblaze B2 dashboard under "App Keys".

2. Create opam-publish config (~/.config/opam-publish/config):

mkdir -p ~/.config/opam-publish
chmod 700 ~/.config/opam-publish

cat > ~/.config/opam-publish/config << 'EOF'
B2_BUCKET=your-bucket
PUBLIC_URL_BASE=https://packages.yourdomain.com
OPAM_REPO_URL=git@your-git-host:path/to/this/repo
EOF

chmod 600 ~/.config/opam-publish/config

Add to PATH#

export PATH="$PATH:/path/to/this/repo/bin"

Or symlink:

ln -s /path/to/this/repo/bin/opam-publish ~/.local/bin/

Publishing a Package#

Prerequisites#

Your OCaml project must have:

  1. A <package>.opam file (or generate via dune build)
  2. A version field in the .opam file or dune-project

Publish#

From your project root:

opam-publish

If multiple .opam files exist, specify which one:

opam-publish mypackage

What Happens#

  1. Validates .opam file (required fields, runs opam lint if available)
  2. Builds a source tarball (excludes .git, _build, _opam)
  3. Computes SHA256 checksum
  4. Uploads tarball to Backblaze B2
  5. Clones this repo, adds package metadata with URL + checksum
  6. Pushes commit to this repo

Example Output#

==> Validating mylib.opam...
    ok
==> Building mylib.1.0.0.tbz...
    42K
==> Uploading to B2...
    https://packages.yourdomain.com/mylib/mylib.1.0.0.tbz
==> Cloning opam repository...
==> Pushing to opam repository...
==> Published mylib 1.0.0

Done. Users can install with:
    opam install mylib

Using This Repository#

Add to OPAM#

opam repository add private git+ssh://git@your-git-host/path/to/repo.git

Or via HTTPS:

opam repository add private git+https://your-git-host/path/to/repo.git

Install Packages#

opam update
opam install <package>

Remove Repository#

opam repository remove private

Releasing a New Version#

  1. Update version in dune-project:

    (version 1.2.0)
    
  2. Regenerate opam file:

    dune build
    
  3. Commit changes:

    git add -A
    git commit -m "Release 1.2.0"
    git tag v1.2.0
    git push origin main --tags
    
  4. Publish:

    opam-publish
    

Cloudflare CDN Setup (Optional)#

To serve packages via a custom domain with Cloudflare:

1. DNS Record#

Add a CNAME record pointing to your B2 bucket:

Type: CNAME
Name: opam (or your subdomain)
Target: f00X.backblazeb2.com  # Replace X with your cluster number from `b2 account get`
Proxy: ON (orange cloud)

2. Transform Rule#

Create a URL rewrite rule in Rules > Transform Rules > Rewrite URL:

Name: B2 Bucket Rewrite
When: (http.host eq "packages.yourdomain.com")
Rewrite to:
  Path: Dynamic
  Expression: concat("/file/your-bucket", http.request.uri.path)

3. SSL Settings#

Set SSL mode to Full in SSL/TLS > Overview.

Create a cache rule for better performance:

URL: packages.yourdomain.com/*
Cache Level: Cache Everything
Edge Cache TTL: 1 month

Troubleshooting#

"version not found"#

Ensure your .opam file has a version field:

version: "1.0.0"

Or in dune-project:

(version 1.0.0)

"version already published"#

Each version can only be published once. Bump the version number.

B2 upload fails#

Check b2 authorization:

uv tool run b2 account get
uv tool run b2 bucket list

Re-authorize if needed:

uv tool run b2 account authorize <applicationKeyId> <applicationKey>

Git push fails#

Ensure you have SSH access to this repository:

ssh -T git@your-git-host

"opam lint failed"#

Fix the issues reported by opam lint. Common problems:

opam lint mylib.opam

Missing fields can be added to dune-project:

(generate_opam_files true)
(name mylib)
(synopsis "Short description")
(authors "Your Name")
(maintainers "your@email.com")
(license MIT)

Then regenerate with dune build.