Skip to content

Latest commit

 

History

History
336 lines (213 loc) · 25.5 KB

deployment.md

File metadata and controls

336 lines (213 loc) · 25.5 KB

Deploy Replica Hub

Replica Hub publishes a public container image at ghcr.io/github/replica-hub. Docker Compose provides the recommended installation experience, while the same image can run on another container platform that meets the deployment contract below.

Requirements

The recommended deployment needs:

  • A server with Docker and the Docker Compose plugin.
  • A domain you control, dedicated to Hub and its previews.
  • Permission to create DNS records and enable HTTPS.
  • A hosting service or reverse proxy that sends the Hub and preview addresses to the same server.

The default deployment runs one Replica Hub container and stores its SQLite databases and Build files in one persistent Docker volume.

Choose an isolated domain

Hub displays prototypes inside its review pages. It requires each preview to use a separate HTTPS subdomain, such as b123.preview.hub.example.com under hub.example.com. This lets preview cookies work while the prototype is embedded. Hub keeps its own sign-in cookie separate.

Browsers treat HTTPS addresses under the same registered domain as one site. For example, hub.example.com, b123.preview.hub.example.com, and other.example.com are all the same site. If another application uses this domain, prototype code can send requests to it and the browser may attach that application's sign-in cookies.

Cookie settings such as SameSite=Lax and SameSite=Strict do not prevent this, because the requests come from the same site. The browser normally stops the prototype from reading other.example.com's responses or cookies limited to that address. It does not stop the prototype from sending these requests.

Use a dedicated domain for Hub. Register a domain, such as example.com, and reserve it entirely for Hub and its previews. Do not host unrelated applications on that domain or any of its subdomains. Giving Hub its own subdomain on a shared domain is not enough.

Hub protects its own sign-in cookies and data. It cannot protect other applications on the domain, especially if they share cookies between subdomains or rely only on SameSite settings.

Choose Hub's address before running ./replica init or creating the GitHub App. Changing it later requires updating Hub and preview settings, GitHub App URLs, DNS records, and HTTPS certificates. Keep the existing encryption secrets; changing the address does not require new ones.

Restrict custom analytics egress

Repository admins can configure custom public HTTPS analytics destinations. By default, any public DNS hostname on HTTPS port 443 is eligible after Hub's IP and TLS checks. To limit recipient domains for your deployment, set an optional comma-separated list of exact lowercase DNS hostnames in the container environment:

REPLICA_ANALYTICS_OUTBOUND_HOSTS=events.example.com,ingest.example.org

This setting is not generated by ./replica init. Add it to the private deployment .env or platform environment, then restart Hub. An empty, malformed, or duplicated entry prevents startup rather than disabling the restriction. Subdomains are not included automatically. Hub rejects private, loopback, link-local, metadata, and other restricted DNS answers on every send and disables HTTP proxies. An allowlist does not replace a network egress firewall: enforce your organization's outbound policy outside Hub too. Query settings are visible and cannot contain credential-like keys; give admins a provider secret for headers or JSON body references instead. See Repository analytics.

Copilot for Learnings

To turn on Learnings, create a fine-grained personal access token whose only permission is Copilot requests, add it to the private deployment .env or platform environment as REPLICA_COPILOT_TOKEN, and restart Hub. A Hub administrator then connects Copilot. ./replica init does not generate this setting. Adding, removing or replacing the token on a running deployment requires stopping every old-configuration web process, job worker, learning command and surviving Copilot SDK/runtime child before starting any new-configuration process; rolling token changes are not supported. Follow token replacement.

Welcome video

Welcome defaults to the YouTube video at https://www.youtube.com/watch?v=J2Z28CwIfsA, embedded through YouTube's privacy-enhanced host. To replace it, set REPLICA_WELCOME_VIDEO_URL in the Hub container environment to a YouTube watch URL or a direct HTTPS video URL. For direct files, use a browser-compatible MP4 with captions; the host must serve the correct content type and support byte-range requests for seeking. Video content is loaded directly by browsers and is not protected by Hub sign-in.

Replace the URL and restart Hub to change the video without changing code or rebuilding the image. Hub's content policy allows only the privacy-enhanced YouTube embed host or, for a direct file, its configured media origin. Use a final media URL that does not redirect to another host. Set the variable to an empty value to leave Welcome without a video while keeping its text and links. Invalid URLs cause an explicit configuration error.

For local development only, the setting also accepts a same-origin path such as /welcome-video.mp4. Keep temporary videos out of Git and remove them after previewing.

Set REPLICA_WELCOME_VIDEO_POSTER_URL to a direct HTTPS image URL to show a still before direct-file playback. The optional setting accepts a same-origin path in development. YouTube supplies its own player controls.

Download the deployment bundle

Choose an explicit hub-v* release from Hub releases. Do not use releases/latest: Hub and canvas releases share this repository, and a canvas release may not contain a Hub bundle.

The example selects hub-v1.0.0-rc.2, a prerelease, not a generally available (GA) release. It contains both replica-deploy.tar.gz and replica-deploy.tar.gz.sha256. No stable Hub release is published yet. For a production deployment, wait for a stable Hub release with both assets, then set HUB_RELEASE to that tag. The curl commands below do not authenticate, so while the repository is private they return HTTP 404 even for readers with repository access.

Run these commands on a Linux server with curl, tar, and sha256sum:

HUB_RELEASE=hub-v1.0.0-rc.2
HUB_RELEASE_URL="https://github.com/github/replica/releases/download/${HUB_RELEASE}"
mkdir replica-hub &&
  cd replica-hub &&
  curl -fsSL --output replica-deploy.tar.gz \
    "${HUB_RELEASE_URL}/replica-deploy.tar.gz" &&
  curl -fsSL --output replica-deploy.tar.gz.sha256 \
    "${HUB_RELEASE_URL}/replica-deploy.tar.gz.sha256" &&
  sha256sum -c replica-deploy.tar.gz.sha256 &&
  tar -xzf replica-deploy.tar.gz

The checksum file records the archive's SHA-256 digest, a value calculated from its contents. Verification must print replica-deploy.tar.gz: OK before extraction. The && chain stops on any failed command. If a download fails or the checksum does not match, stop and do not run setup. Check the selected release and download both files again into a new directory; never skip verification.

The bundle contains compose.yml and the replica command. You do not need to clone the source repository.

The rc.2 bundle defaults to the major-version image tag ghcr.io/github/replica-hub:1. Verifying its archive does not verify that image or establish GA deployment readiness. Before production setup, confirm the stable release's assets and public image are available as described in Publish a release.

Create the configuration

Run:

./replica init

Enter the public hostname for Hub:

Replica Hub hostname: hub.example.com

The command downloads the Hub image selected by the deployment bundle and creates a private .env file containing the generated origins and secrets. It prints the setup code that authorizes the first GitHub App connection. Keep .env private and keep using the same file for the lifetime of the deployment.

Do not run initialization again for an existing deployment. New encryption keys cannot read GitHub credentials stored with the previous keys.

Configure DNS and HTTPS

Create DNS records for:

hub.example.com
*.preview.hub.example.com

Point both records to the Replica Hub server and enable HTTPS for both addresses. Configure the hosting service or HTTPS proxy to forward both addresses to port 3000 on the server. Replica Hub listens only on 127.0.0.1:3000 by default.

The wildcard address gives every Build an isolated browser origin:

Hub:               https://hub.example.com
Signed-in preview: https://b123.preview.hub.example.com
Public share:      https://s123.preview.hub.example.com

Most hosting services configure request forwarding automatically. A custom reverse proxy must preserve the original host and HTTPS scheme.

Client IP addresses and trusted proxies

Hub records client IP addresses in security audit logs and limits the number of requests from each address. A proxy is a server that forwards requests to Hub. If Hub records the proxy's address instead of the client's, everyone using that proxy may share one request limit.

How Hub determines the client IP

  • Direct requests: Hub uses the address of the client connecting to it.
  • Requests through a trusted proxy: The proxy sends HTTP headers listing the client's address and the servers that forwarded the request. Hub uses Rails, its web framework, to choose the client address from that list. Hub only uses this list when the server connecting to it has an address Rails is configured to trust.

Set up your proxies

Incorrect proxy settings can make Hub record the wrong address or accept an address supplied by the client. Configure your hosting service and proxies to:

  1. Replace client-supplied address headers. The first server accepting public HTTPS requests must replace those headers with addresses it has checked.
  2. Trust only your forwarding servers. Later proxies may add their addresses to the list. Hub's built-in proxy, Thruster, does this when another server provides HTTPS.
  3. Block direct access around the proxy. Only the intended forwarding servers should be able to connect to Hub.

Rails trusts local and private network addresses by default. An address in those ranges does not prove the caller is a proxy. If a proxy keeps address headers supplied by the client, Rails may skip the client's actual private address and choose a fake one from those headers.

Configuring trusted proxy ranges

Rails lists the addresses it trusts in config.action_dispatch.trusted_proxies. The official Hub image has no environment variable for this setting, and ./replica init does not set it. If you need different trusted addresses, build a custom Hub image with that Rails setting.

Trust only your proxy addresses or network ranges, never all addresses. Adding an unsupported variable to .env will not change this setting.

Azure Front Door and other public-address proxies

Azure Front Door and some other proxies use public IP addresses that Rails does not trust by default. Hub may choose one of those addresses instead of the client's, even when the final connection comes from a trusted local proxy.

REPLICA_AZURE_FRONT_DOOR_ID checks that the request names your Front Door profile. It does not change how Hub chooses the client IP. You must also restrict server access as described in Azure Front Door.

Verify your deployment

Before relying on audit IP addresses or request limits, check your running deployment:

  1. Check the recorded address. Make a browser request that produces a security audit event. Compare its recorded IP with the client's known public IP, not a proxy's address.
  2. Try supplying a fake address. Repeat the request with a different address in the forwarding headers. Hub must still record the real client IP and apply the same request limit.
  3. Check for direct access. Confirm that clients cannot bypass the public HTTPS server and connect directly to Hub.

Use the actual hosting service and proxies for these checks. Local Rails tests cannot prove that the deployed proxies are configured correctly.

If Hub records the wrong address, fix the proxy settings before relying on audit logs or per-address request limits.

See Security audit for the information Hub records.

Start Replica Hub

Run:

./replica up

The command starts the configured Hub image. Docker downloads the image if it is not already present. The command also waits for the health check and prints the setup address. The container restarts automatically after a server reboot.

Open:

https://hub.example.com/setup

Enter the setup code printed during initialization. Replica Hub then creates a private GitHub App and sends you to GitHub to install it on the prototype repositories Hub may use. See GitHub App setup for the requested permissions and credential lifecycle.

Prepare a prototype repository

A compatible repository needs:

  • package.json.
  • package-lock.json.
  • A non-empty npm build script.
  • dist/index.html after the build.

Replica Hub can open a pull request that installs the required GitHub Actions workflow. After that pull request is merged, pushes to non-default prototype branches produce Builds and screenshots for Hub. See GitHub Actions builds for output limits and workflow behavior.

Operate the deployment

Use the wrapper instead of calling Docker Compose directly:

Goal Command
Start Hub ./replica up
Stop Hub without deleting data ./replica down
Show health ./replica status
Follow application logs ./replica logs
Install a compatible update ./replica update

Updates are intentional. Replica Hub does not install new images automatically, and ./replica update never moves the deployment to a new major version.

Contain public sharing

Public sharing is off by default on both new and upgraded deployments. The public-sharing upgrade permanently revokes existing links and pending preview handoffs. An enrolled Hub administrator can enable new links from /sharing_settings, with fresh GitHub proof and explicit confirmation. Disabling later revokes all links again. See Share links before upgrading a deployment that uses public URLs; do not promise those URLs will survive the upgrade.

For example, after an externally reported account suspension, a Hub administrator can disable public sharing for the entire deployment. This blocks creation, existing links, and public-preview files, including requests using previously issued public-preview cookies. It cannot recall bytes already delivered.

This is manual containment, not suspension detection or automatic offboarding. It does not revoke browser Sessions or canvas credentials; ending all sessions is a separate action. A failed status check or a 404 alone is not proof of account suspension.

Before relying on the control, verify its default, administrator authorization, creation denial, existing-link and preview-file denial, and permanent revocation after re-enabling on the deployed version. Security acceptance for #166 remains pending. See Authentication and repository access for current behavior and Security for evidence limits.

Enable a maximum share-link lifetime

Public share links have a seven-day maximum when REPLICA_SHARE_LINK_MAX_DAYS is unset. Set it to 1, 7, or 30 to choose a different maximum or make the default explicit:

REPLICA_SHARE_LINK_MAX_DAYS=7
  1. Choose an image or source version that includes this setting. Add it to the deployment's private .env or platform environment, not to a prototype repository. ./replica init does not generate it.
  2. Warn authors that upgrading or shortening the maximum can stop old links immediately. The default also applies when upgrading with the setting unset. Schedule a restart. Keep the existing encryption secrets; do not run initialization again.
  3. With the Compose bundle, run ./replica down, then ./replica up. This retains deployment data. On another platform, restart every Hub web and background process with the same setting.
  4. Check health and logs. Before serving requests, Hub automatically shortens missing or longer saved expiry dates to the link's original creation time plus the maximum. This includes expired and revoked records and runs in a transaction on every application startup. Compliant dates remain unchanged; no separate maintenance command is needed. Empty or unsupported settings and failed database updates prevent startup rather than leave activation incomplete.
  5. Using authorized test links and a signed-out browser, check that one-day and seven-day choices work. Thirty-day and never-expiring creation must be rejected. A link created at least seven days ago must return not found.
  6. Check that the old link's preview files and controls cannot load, including with a previously issued public-preview cookie. See Link lifetime for the checks Hub makes.
  7. Record the deployed version and results. Do not save public URLs, cookies or credentials in the audit notes.

This setting limits public-link lifetime. It does not turn public sharing on, automatically offboard a person, or establish Security acceptance.

Change the maximum

Restart every Hub web and background process with the same setting after a change. The maximum always starts at each link's original creation time, not the restart time.

Configuration change Existing links New links
Choose a shorter maximum Startup permanently shortens any later deadline. Links already as old as the new maximum expire immediately. Earlier deadlines and revocations remain unchanged. Only lifetimes within the new maximum are permitted.
Choose a longer maximum Saved deadlines do not move later; expired links cannot revive. Longer choices become available, up to the new maximum.
Remove the setting The maximum returns to seven days, not unlimited sharing. Deadlines longer than seven days are shortened; earlier deadlines remain unchanged. One-day and seven-day choices are permitted.

For example, changing 30 to 7 gives a six-day-old link at most one day left. Changing back to 30 cannot restore its old deadline. Removing a 1 setting lets authors create new seven-day links, but existing one-day links still expire at their saved deadlines. No setting permits new never-expiring links.

Offboard a person or contain a reported suspension

Use this manual procedure after an authorized organization administrator confirms that a person has left or that GitHub has suspended their account. Losing repository access means the person can no longer read that repository. Suspension means GitHub has restricted the account itself. A Hub error or a 404 alone does not prove suspension or completed offboarding.

These controls affect everyone. Disabling sharing permanently stops every public link. Ending all Hub sessions signs everyone out, including the operator, and ends every canvas's Hub connection. Local prototypes can still run. Coordinate the interruption and keep another authorized Hub administrator available.

Before a planned departure, hand off Hub administration. If the departed person was the only administrator, the deployment owner must use last-administrator recovery before continuing. Do not remove the last role or reset the App to recover access.

Public repositories remain readable on GitHub after organization offboarding. A public App may also let a former member sign in again. If GitHub's access rules cannot exclude the account, keep public sharing disabled and escalate through the approved security process. See App ownership and repository visibility.

The actions below may show Verify with GitHub. Complete that check, return to the same page, and confirm the action again. Verification alone does not perform it. See Fresh GitHub authorization.

  1. Confirm the person's GitHub account and the report through your organization's approved process. Record who authorized the action and which Hub version is deployed.
  2. Have a GitHub administrator remove the person's organization, team and direct repository access as required.
  3. Keep the organization's GitHub App private to limit new sign-ins to organization members. Making the App private does not end existing Hub sessions or canvas connections.
  4. While signed in as a Hub administrator, open /sharing_settings, disable Public sharing, and confirm. Old public links and preview cookies stop working. Files already delivered to a visitor cannot be recalled.
  5. If the person is a Hub administrator, remove their role at /hub_admins before signing everyone out. Hub cannot remove its final administrator. Follow administrator removal. Removing the role alone does not end ordinary repository access.
  6. Open /session_revocation, choose End all Hub sessions, read the warning that includes your own sign-out, and confirm. Browser and canvas credentials are ended. Pending sign-ins and connection approvals are cancelled.
  7. If a live connection fails to disconnect, its saved credentials are still ended. Check the reported failure and follow session-revocation behavior.
  8. Sign in again as an authorized administrator. Confirm that public sharing is disabled. Check the audit records for sharing disable, administrator removal when applicable, and ending sessions.
  9. Using authorized test accounts and links, confirm that old public URLs and previews return not found and old browser/canvas credentials no longer work. Never collect or reuse the former person's credentials.
  10. Confirm that the intended audience can sign in again and that GitHub's App rules exclude the offboarded account. Ending sessions does not permanently block legitimate new sign-in.
  11. Restore public sharing only after authorized review. Only new links can be created; old revoked links never return. Keep any configured maximum in place. Record the results and any unresolved gaps in the approved offboarding or incident record.

Do not wait for Hub to discover lost repository access. Hub stops a creator's links only after it asks GitHub and confirms that the creator can no longer read that repository. Public visits do not trigger that check, and Hub does not run it continuously. See Creator access loss.

Hub has no automatic or per-person offboarding action. This procedure and the maximum lifetime do not complete #166 or establish Security acceptance.

Use the image directly

Container platforms can use the same public image without the Compose bundle:

Image:             ghcr.io/github/replica-hub:<image-tag>
Container port:    80
Health check:      /up
Persistent path:   /rails/storage
Background jobs:   SOLID_QUEUE_IN_PUMA=1

Choose an image tag from Hub releases. For an exact image tag, omit the release tag's hub-v prefix. Replace <image-tag> below with that tag and use the same image to generate configuration and run Hub:

docker run --rm "ghcr.io/github/replica-hub:<image-tag>" \
  bin/replica-deployment-config generate \
  --host hub.example.com \
  --format json

Use --format dotenv for an environment file. The command prints configuration to standard output and writes nothing to the host. Every run creates new secrets.

The deployment platform must provide persistent local storage, run exactly one Replica Hub container, and route the Hub and wildcard preview addresses to it over HTTPS. Platforms built around replaceable containers without persistent local storage need a different database and artifact-storage design that Replica Hub does not yet provide.

Image versions

Stable releases publish these tags:

Tag Format Behavior
Exact MAJOR.MINOR.PATCH Never changes
Minor MAJOR.MINOR Receives compatible patch releases
Major MAJOR Receives releases within that major
Latest latest Tracks the newest stable release

Prereleases publish only their exact version tag. The official image embeds the matching pinned Replica build action, so repository workflow installation does not require another version setting.

Troubleshoot startup

Check the current state:

./replica status

Inspect application output:

./replica logs

./replica up exits unsuccessfully and prints recent logs when the application cannot become healthy. Common causes include an invalid hostname, changed encryption secrets, an unavailable local port, or a data volume the container cannot write.

If port 3000 is unavailable, add another local port to .env before starting Hub:

REPLICA_PORT=3001

Configure the HTTPS proxy to forward requests to the same port.