Set up a private Office callback route
Use this guide for a prepared physical test runtime that uses a remote Office renderer. The callback lets the renderer retrieve documents from the target BFF through the existing shared HTTPS proxy.
Before you start
Prepare the runtime with the separate test environment guide. Use an absolute physical directory, without symlinks. The target must contain .env and compose.yaml, and its instance identity must differ from the original root.
Keep the existing shared Nginx Proxy Manager (NPM) healthy and running, with its existing HTTPS port mapping. Run local Tailscale with the existing listener reachable through this machine's Tailscale address. The setup checks that address and listener before it creates resources. It creates no proxy or listener and transfers no production route.
For setupOfficeCallback.sh, keep the existing CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_ZONE_ID, and CLOUDFLARE_TOKEN in the original source root. The callback command reads management credentials from that root, so copying them to the target merely for callback setup is unnecessary. The selected active zone must belong to that account and contain the callback hostname. The token needs the DNS access for certificate issuance and the owned DNS record. Do not copy these management credentials into the target application service environments.
This source-root rule applies to callback setup only. A separately provider-enabled runtime can require private management settings in its own lifecycle root .env. With Microsoft enabled, startup requires its own Graph callback URLs and complete Cloudflare management settings. See Microsoft prerequisites for a separate snapshot. These lifecycle settings do not belong in application service environments.
The original root must also select the existing NPM container and administrator credentials. Use a real email address for the authenticated NPM administrator. NPM uses that user's email for the certificate contact and invokes certbot with Terms of Service agreement. The setup rejects malformed addresses and reserved local or example domains before DNS or certificate changes. This bounded check does not prove external email deliverability.
The NPM user's email is also its administrator login. When you change the certificate contact, update that same user's email. Keep its existing password and roles unchanged. Synchronize NPM_ADMIN_EMAIL everywhere that account's management credentials are used, including the original root and any test runtime that uses them.
In the target configuration, set REMOTE_WOPI_ALLOWED_CIDRS to the actual renderer Tailscale IPv4 addresses as /32 CIDRs within 100.64.0.0/10. Arbitrary private RFC1918 addresses are not accepted by this setup. Use a comma-separated list when more than one renderer sends callbacks. The renderer must have private network access to the local Tailscale address and existing HTTPS port.
Select the callback name
Set these values in the selected target configuration:
ECS_ORIGIN_DEPLOYMENT_ID=ttrmb
ECS_PUBLIC_HOST_PREFIX=test
WOPI_CALLBACK_DOMAIN_SUFFIX=ecs.t42.hu
With NPM_HTTPS_PORT=8443, the origin calculation produces:
REMOTE_WOPI_ORIGIN=https://test.wopi.ttrmb.ecs.t42.hu:8443
WOPI_CALLBACK_MODE=private
The suffix is optional. When it is unset or empty, the origin calculation preserves the previous callback modes and settings. See the callback environment reference.
Run the explicit setup
From the original project root, run:
bash setupOfficeCallback.sh --target-runtime /absolute/physical/runtime
Replace /absolute/physical/runtime with the prepared test runtime directory. The command never selects production as a default target.
The setup creates an exact-owned, DNS-only A record that points to the verified local Tailscale IPv4 address. It obtains a public certificate through the DNS-01 challenge, or reuses a valid installed certificate. Reuse requires a valid public chain, hostname coverage, sufficient remaining validity, and a matching private key.
The setup creates a separate private callback host in the existing NPM. It forwards only the narrow WOPI document GET routes to the target BFF and restricts sources to the configured renderer CIDRs. Other paths do not expose the BFF application.
The command reports that the callback is configured. It does not switch the browser Office mode or prove a complete application journey. Apply the ordinary hybrid placement transition separately. See private placement variants.
A public DNS name and public certificate do not make a private IP address publicly reachable. This setup does not change the remote renderer's trust store.
Check the renderer journey
Permit the additional callback origin
The remote Coolify Collabora service must trust the callback host separately from the browser origin. Keep required WOPI_CALLBACK_ORIGIN in aliasgroup1 and retain all existing approved origins. The optional WOPI_ADDITIONAL_CALLBACK_ORIGIN supplies aliasgroup2:
aliasgroup1: ${WOPI_CALLBACK_ORIGIN:?Reviewed callback origin required}
aliasgroup2: ${WOPI_ADDITIONAL_CALLBACK_ORIGIN:-}
| Variable | Consumer | Purpose | Type or format | Required when | Default | Example | Constraints or security |
|---|---|---|---|---|---|---|---|
WOPI_ADDITIONAL_CALLBACK_ORIGIN | Remote Coolify Collabora aliasgroup2 | Trust one additional WOPI callback origin | Canonical exact HTTPS origin, including the selected port | A second callback origin is needed | Empty. Adds no host. | https://test.wopi.ttrmb.ecs.t42.hu:8443 | No wildcard, credentials, or path. Preserve primary aliasgroup1 and existing approved origins. |
The example comes from the derived DNS setup above. The remote operator copies the actual derived REMOTE_WOPI_ORIGIN once into the remote environment as WOPI_ADDITIONAL_CALLBACK_ORIGIN. Local origin calculation does not update the remote Coolify environment automatically.
Keep Coolify's persisted raw Compose, generated Compose, service environment, and rendered container environment consistent with this mapping and value. Before recreation, check the exact owned Collabora resource and service. Recreate only that verified Collabora service from the persisted configuration. Preserve all other containers, existing data, and the proof key.
Collabora supports numbered aliasgroupN environment variables through --use-env-vars. In initializeEnvOptions, an empty alias-group string produces no host entry.
Permit browser embedding
The remote Collabora renderer must permit the exact browser MAILGEN_PUBLIC_URL origin in the frame-ancestors directive of net.content_security_policy. The browser origin includes the scheme, hostname, and port. It differs from the callback origin (REMOTE_WOPI_ORIGIN). Callback host trust, browser CSP, and DNS/TLS are separate prerequisites. Configure both callback trust and browser embedding. DNS/TLS success alone satisfies neither policy.
In the renderer's coolwsd.xml, the following example shows the setting inside <net> for a policy with no existing directives:
<net>
<content_security_policy>frame-ancestors https://browser.example.invalid:8443;</content_security_policy>
</net>
Replace https://browser.example.invalid:8443 with the exact browser MAILGEN_PUBLIC_URL origin. For an existing policy, add that origin to its current frame-ancestors list. Retain every existing approved origin and all other directives. Do not replace the existing policy with this example. net.frame_ancestors is obsolete.
For the current prepared Coolify renderer, docker/coolify/compose.yaml requires COLLABORA_CONTENT_SECURITY_POLICY. Set the full reviewed content security policy (CSP), including the exact approved browser origin. Preserve existing approved origins and all other directives.
| Variable | Consumer | Purpose | Type or format | Required when | Default | Example | Constraints or security |
|---|---|---|---|---|---|---|---|
COLLABORA_CONTENT_SECURITY_POLICY | Remote Coolify collabora service | Set net.content_security_policy for browser embedding | Full reviewed CSP string | Current prepared Coolify renderer | None. Unset or empty values block Compose interpolation. | frame-ancestors https://browser.example.invalid:8443; | Replace the example with the exact approved browser origin. Preserve existing approved origins and directives. The example alone applies only when no other policy exists. |
The Compose command array contains one item:
command:
- "--o:net.content_security_policy=${COLLABORA_CONTENT_SECURITY_POLICY:?Reviewed renderer CSP required}"
The array preserves spaces in the policy as one argument and avoids shell quoting. The renderer entrypoint runs /usr/bin/coolwsd --use-env-vars directly. Keep the existing extra_params unchanged.
Persist the variable and command configuration in the actual Coolify service settings. A change only inside the running container does not survive recreation. Have the responsible operator recreate only the owned collabora service from those persisted settings. Preserve unrelated services, existing data, and the proof key. This configuration prerequisite does not establish live rendering acceptance.
From the real renderer environment, check DNS resolution to the intended Tailscale address and TLS hostname and chain acceptance. Then check a real callback and the complete Office application journey in the selected runtime. Acceptance remains pending until an actual document loads in the root application. A successful setup command, certificate issuance, or dependency probe does not replace these checks.
Keep browser certificate trust separate from renderer trust. The browser hosts can still need their existing local certificate setup.
Retain resources and switch placement
The target keeps private ownership state at .docker-state/office-callback/ownership.json. Its persisted nonce, resource identities, and operation intents establish ownership. Preserve this file with the runtime. The file does not contain the Cloudflare token. NPM stores the token through its standard DNS-01 credential mechanism for certificate renewal.
Repeat setup safely reuses exact-owned resources. It refuses foreign resources and unexpected changes. A normal FullLocal transition disables only the owned callback host. A later hybrid transition re-enables it. DNS and certificates remain available for reuse.
Stop the selected instance before switching private placement variants. Preserve its data, keys, and volumes. FullLocal keeps Office local even when the optional callback name remains configured.
Recover after incomplete certificate issuance
A saved certificate_intent blocks a duplicate certificate POST. A retry can reuse a valid installed certificate after a lost response. Do not clear an uncertain or pending intent to force another issuance.
- Inspect the selected NPM certificate records for the exact callback domain and any certificate ID in the target's ownership file. Compare the owned route and domain with that file.
- Inspect NPM issuance logs and active certbot processes for that domain. If issuance remains active or its outcome is uncertain, preserve the intent and resolve that state first.
- Only after a known failed attempt, confirm that no matching certificate exists and no issuance remains active. Correct the reported cause, including the NPM contact and synchronized login settings if needed.
- Preserve a private backup of the target's
.docker-state/office-callback/ownership.jsonbefore editing it. Use a private directory with mode0700and a backup file with mode0600. Remove only the failedcertificate_intentfield from the owned JSON. Preserve the identity, nonce,dns_id, and all other fields. This is a guarded manual recovery, not a separate product command. - Rerun
bash setupOfficeCallback.sh --target-runtime /absolute/physical/runtimefrom the original project root with the same target.
Never delete the whole ownership file or foreign certificates. Preserve production routes and all unrelated proxy resources.