|
| 1 | +# Sharing A Project Publicly |
| 2 | + |
| 3 | +`warden share` exposes a running project to the public internet through a share provider, without deploying anywhere. This is useful for: |
| 4 | + |
| 5 | +- Receiving webhooks or payment provider callbacks that need a publicly reachable URL |
| 6 | +- Showing work in progress to a client or teammate |
| 7 | +- Testing on a real mobile device over a real domain instead of `.test` |
| 8 | + |
| 9 | +## How It Fits Warden's Architecture |
| 10 | + |
| 11 | +A share provider runs as a global service, started with `warden svc up` alongside Traefik and the other {doc}`global services <../services>`. The provider's container joins the `warden` network and forwards incoming public traffic to Traefik, which then routes it exactly the way it routes local `.test` traffic: by matching the `Host` header to the project's Nginx or Varnish container. |
| 12 | + |
| 13 | +``` |
| 14 | +Internet -> provider edge -> share agent (warden network) -> Traefik -> Nginx/Varnish |
| 15 | +``` |
| 16 | + |
| 17 | +Because routing happens at the Traefik layer, sharing a project does not change how the project itself is configured. The same container serves both `myproject.test` and the public hostname. |
| 18 | + |
| 19 | +## User Configuration |
| 20 | + |
| 21 | +### Global, Per-Developer (`~/.warden/.env`) |
| 22 | + |
| 23 | +* `WARDEN_SHARE_PROVIDER` selects the share provider, for example `cloudflared`. |
| 24 | + |
| 25 | +This setting belongs in `~/.warden/.env`, not in any project's `.env`. `~/.warden/.env` lives outside every project repository, so the tunnel credentials and tunnel identity it references stay tied to the developer's own account rather than being shared or committed to a project. |
| 26 | + |
| 27 | +### Per Project (`.env`) |
| 28 | + |
| 29 | +* `TRAEFIK_PUBLIC_DOMAIN` is the public hostname for the project, for example `myproject.example.com`. |
| 30 | + |
| 31 | +The value must be a plain hostname: letters, digits and hyphens, with at least one dot. `warden env up` rejects anything else, since the value is used verbatim in a Traefik router rule and in the provider's ingress configuration. |
| 32 | + |
| 33 | +If a developer wants to use a personal hostname without committing it to the project, `TRAEFIK_PUBLIC_DOMAIN` can be set in `.env.local` instead of `.env` (see {doc}`../environments/customizing`). |
| 34 | + |
| 35 | +Projects running Varnish are routed through Varnish automatically; no separate configuration is needed. |
| 36 | + |
| 37 | +## Cloudflare Tunnel Provider |
| 38 | + |
| 39 | +### Prerequisites |
| 40 | + |
| 41 | +- A Cloudflare account |
| 42 | +- A domain (zone) on Cloudflare |
| 43 | +- Docker |
| 44 | + |
| 45 | +### Setup |
| 46 | + |
| 47 | +1. Set the provider in `~/.warden/.env`: |
| 48 | + |
| 49 | + ```{code-block} bash |
| 50 | + WARDEN_SHARE_PROVIDER=cloudflared |
| 51 | + ``` |
| 52 | + |
| 53 | +2. Authenticate with Cloudflare: |
| 54 | + |
| 55 | + ```{code-block} bash |
| 56 | + warden share login |
| 57 | + ``` |
| 58 | + |
| 59 | + This opens a browser for Cloudflare authentication. On success a `cert.pem` is stored in `~/.warden/etc/cloudflared/`. |
| 60 | + |
| 61 | +3. Create a tunnel: |
| 62 | + |
| 63 | + ```{code-block} bash |
| 64 | + warden share create [name] |
| 65 | + ``` |
| 66 | + |
| 67 | + The tunnel name defaults to `warden`. This writes `WARDEN_CLOUDFLARED_TUNNEL_ID` into `~/.warden/.env`, and adds `WARDEN_SHARE_PROVIDER=cloudflared` to that file if it is not already set. |
| 68 | + |
| 69 | +4. Create the DNS record. In the Cloudflare dashboard, add a proxied CNAME record for the hostname (and for `*.hostname` if subdomains are used) pointing to `<tunnel-id>.cfargotunnel.com`. This can also be done with `cloudflared tunnel route dns`. Warden does not create this record. |
| 70 | + |
| 71 | +5. Start global services: |
| 72 | + |
| 73 | + ```{code-block} bash |
| 74 | + warden svc up |
| 75 | + ``` |
| 76 | + |
| 77 | +6. Set the public hostname in the project's `.env`: |
| 78 | + |
| 79 | + ```{code-block} bash |
| 80 | + TRAEFIK_PUBLIC_DOMAIN=myproject.example.com |
| 81 | + ``` |
| 82 | + |
| 83 | +7. Start the project: |
| 84 | + |
| 85 | + ```{code-block} bash |
| 86 | + warden env up |
| 87 | + ``` |
| 88 | + |
| 89 | +8. Check status: |
| 90 | + |
| 91 | + ```{code-block} bash |
| 92 | + warden share status |
| 93 | + ``` |
| 94 | + |
| 95 | + This prints the configured provider, the tunnel ID, whether the container is running, and the list of domains currently connected. |
| 96 | + |
| 97 | +### Configuration Regeneration |
| 98 | + |
| 99 | +The provider's ingress configuration is regenerated from the set of domains carrying the `dev.warden.share.domain` label on any running container. This happens on every `warden env up`, `warden env down`, `warden env start`, `warden env stop`, and on `warden svc up`. The share agent container is restarted only when the regenerated ingress list actually changed, so starting or stopping one project does not interrupt tunnels for other projects. |
| 100 | + |
| 101 | +`warden share update` forces a regeneration and restart. `warden share delete` removes the tunnel from Cloudflare. `warden share logout` removes all cloudflared credentials and configuration from `~/.warden/etc/cloudflared/`; if a tunnel is still configured it asks for confirmation first, since the tunnel would otherwise be left orphaned on the Cloudflare side. |
| 102 | + |
| 103 | +Cloudflared files live under `~/.warden/etc/cloudflared/`: `cert.pem` (Cloudflare account credential), the tunnel credentials file, and the generated `config.yml`. |
| 104 | + |
| 105 | +:::{note} |
| 106 | +The generated tunnel configuration sets `noTLSVerify: true` on the connection between the share agent and Traefik. This is expected: it applies only to the internal Docker network segment, where the agent is trusting Warden's self-signed Traefik certificate. The public-facing leg of the connection is terminated with Cloudflare's own edge TLS certificate. |
| 107 | +::: |
| 108 | + |
| 109 | +### Troubleshooting |
| 110 | + |
| 111 | +- **Warning about a missing tunnel on `warden svc up`**: `WARDEN_SHARE_PROVIDER` is set but no tunnel has been created yet. Run `warden share login` and `warden share create`. |
| 112 | +- **404 from Cloudflare**: the DNS record is missing, or the project's domain is not in the generated ingress list. Check `warden share status` for the connected domains. |
| 113 | +- **`TRAEFIK_PUBLIC_DOMAIN` rejected on `warden env up`**: the value must match a plain hostname pattern (letters, digits, hyphens, at least one dot). |
| 114 | + |
| 115 | +## Adding A Provider |
| 116 | + |
| 117 | +A share provider is added as `utils/share/<name>.sh` plus a `docker-compose.share-<name>.yml` file. The script must implement six functions: `shareProviderIsConfigured` (whether the provider has enough state to run), `shareProviderComposeFile` (path to the compose file to include), `shareProviderPreflight` (warnings shown on `svc up`), `shareProviderRegenerateConfig` (rewrite the provider's configuration from the current domain list), `shareProviderStatus` (output for `warden share status`), and `shareProviderCommand` (handling for any provider-specific subcommands). |
0 commit comments