DexioView only
Sign inMake a copy

Dexio / how-we-build-dexio / app

Open-sourcing the server behind a hosted product

The Dexio server is public at https://github.com/dexio-wiki/dexio. The hosted service at app.dexio.wiki runs the same code: app-server-and-releases. Its MCP endpoint: remote-mcp-server-with-oauth.

What it is

Why it is built this way

How to build it

  1. Audit the tree, not the history: scan for credentials, private names and addresses in fixtures and docs, and anything that points at internal systems. Fix it in the tree.

  2. Rename the old repository (ours is -archive) and keep it private. Create the public repository with one commit of the cleaned tree, authored with your GitHub no-reply address (<id>+<user>@users.noreply.github.com). If your shell exports GIT_AUTHOR_EMAIL, that overrides the repo's config; set it per commit.

  3. Add LICENSE (AGPL-3.0), SECURITY.md with an address for reports, and deploy/ with the Compose file, a reverse proxy config and .env.example.

  4. Make every hosted-only feature optional and off by default. Our examples: with no Stripe key the server sells no plans and enforces no member or storage limits; analytics loads only on our own domain, so a self-hosted copy sends nothing to Google; the public URL used in OAuth and emails comes from one variable.

  5. Add the image workflow (.github/workflows/image.yml): build the runtime stage for amd64, start it with nothing configured, check /healthz, a sign-up and the OAuth issuer, then build and push both architectures:

    - name: Smoke test
      run: |
        docker run -d --name dexio -p 8080:8080 -e DEXIO_PUBLIC_URL=http://127.0.0.1:8080 dexio:smoke
        for i in $(seq 1 30); do curl -fsS http://127.0.0.1:8080/healthz && break; sleep 1; done
        issuer=$(curl -fsS http://127.0.0.1:8080/.well-known/oauth-authorization-server | jq -r .issuer)
        test "$issuer" = http://127.0.0.1:8080
    - uses: docker/metadata-action@v5
      with:
        images: ghcr.io/dexio-wiki/dexio
        tags: |
          type=raw,value=latest,enable={{is_default_branch}}
          type=sha,format=long,prefix=
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
    
  6. Make the package public. GitHub creates it private. In an organization, the owner first allows public packages (Settings, Packages, Package creation), then changes the package's visibility in the web UI; there is no API for it. A public package cannot be made private again, and public packages are free.

  7. Point the hosted release pipeline at the new repository and release once. Check health, a sign-in, and that the OAuth issuer is unchanged, since every connected client compares it.

Verify

Pitfalls we hit