源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
# Organization Install Links
|
||||
|
||||
Status: self-host operator guide
|
||||
|
||||
Owner: platform/self-host
|
||||
Related: `ee/apps/den-api/src/install-links.ts`, `ee/apps/den-api/src/routes/org/install-links.ts`, `ee/apps/den-api/src/desktop-connect-grants.ts`, `packaging/helm/openwork-ee/README.md`
|
||||
|
||||
## What users get
|
||||
|
||||
An organization install link opens a three-step Den page:
|
||||
|
||||
1. Download and run the standard OpenWork installer.
|
||||
2. Return to Den and click **Open OpenWork**.
|
||||
3. Confirm the exact organization and server in the app, then complete normal
|
||||
organization sign-in.
|
||||
|
||||
This flow does not build a customer-specific installer or ZIP. Branding and
|
||||
server configuration are applied only after the user confirms the deep link.
|
||||
Possession of the install link never signs a user in or grants workspace
|
||||
access.
|
||||
|
||||
The guided flow is scoped to Den organization install links. The public
|
||||
`/desktop` and landing-page downloads keep their existing behavior.
|
||||
|
||||
## Upgrade checklist
|
||||
|
||||
> **Default:** Install links are active for every organization on every
|
||||
> deployment. `DEN_INSTALL_LINKS_GATING_ENABLED` is deprecated and inert;
|
||||
> platform admins can turn an org dark from `/admin` as a kill switch.
|
||||
|
||||
### Helm
|
||||
|
||||
1. Keep `migrations.enabled=true` for the upgrade. The migration Job creates
|
||||
both `install_link` and `desktop_connect_grant`.
|
||||
2. Keep `config.public.connectLinkMode: exchange` (the default). No key is
|
||||
required.
|
||||
3. Restart or roll the deployment. No install-link capability toggle is
|
||||
required for a normal self-hosted installation.
|
||||
4. No hosted-style rollout flag is required: install links are on by default.
|
||||
`DEN_INSTALL_LINKS_GATING_ENABLED` is deprecated and inert; configure
|
||||
`config.public.bootstrapAdminEmails` only if platform admins need `/admin`
|
||||
access to turn an org dark as a kill switch.
|
||||
|
||||
### Docker Compose
|
||||
|
||||
Run the migration once, then restart Den:
|
||||
|
||||
```bash
|
||||
docker compose -f packaging/docker/docker-compose.den-dev.yml exec den sh -lc \
|
||||
"node /app/ee/packages/den-db/dist/scripts/bootstrap.js"
|
||||
```
|
||||
|
||||
If the stack has a custom Compose project name, include the same `-p <project>`
|
||||
flag.
|
||||
|
||||
## Public origins
|
||||
|
||||
Set `DEN_BASE_URL` to the externally reachable Den web origin, for example
|
||||
`https://openwork.example.com`. Den derives Better Auth, CORS/trusted origins,
|
||||
web-app hosts, API defaults, and MCP resource defaults from that one value. Set
|
||||
`DEN_API_PUBLIC_URL` only as a compatibility override when you publish a separate
|
||||
Den API origin or an externally reachable proxy path, such as
|
||||
`https://openwork.example.com/api/den`. If you publish a separate Den API origin,
|
||||
the install-link exchange and external MCP clients must be able to reach it.
|
||||
|
||||
Invitation acceptance links use the first non-wildcard trusted origin. In the
|
||||
single-origin setup this is derived from `DEN_BASE_URL`; set
|
||||
`DEN_BETTER_AUTH_TRUSTED_ORIGINS` only when you need extra web origins during a
|
||||
migration.
|
||||
|
||||
## Installer delivery
|
||||
|
||||
Customer-facing planning guidance for this section is published at
|
||||
[`packages/docs/start-here/installer-delivery.mdx`](../packages/docs/start-here/installer-delivery.mdx).
|
||||
This repository page remains authoritative for implementation and security
|
||||
details.
|
||||
|
||||
Den first validates the organization install token. It then chooses exactly
|
||||
one of these paths:
|
||||
|
||||
| Deployment | Download behavior | Required client access |
|
||||
|---|---|---|
|
||||
| Internet-connected | Immediate `302` to the exact standard asset under `OPENWORK_INSTALLER_RELEASE_REPO` and `OPENWORK_INSTALLER_RELEASE_TAG`. The organization token is not forwarded. | Den web/API, `github.com`, `release-assets.githubusercontent.com`, and `objects.githubusercontent.com` for legacy or rollback paths. |
|
||||
| Semi-air-gapped | Stream the matching standard installer from `OPENWORK_INSTALLER_ARTIFACTS_DIR`. There is no ZIP or in-memory whole-file buffer. | Den web/API only. |
|
||||
| Fully internal installer delivery | Same mounted-artifact path, with Den web/API and the installer artifact available entirely inside the isolated network. This describes installer bytes only, not full product isolation. | Internal Den web/API only. |
|
||||
|
||||
The standard filenames use the release tag without a leading `v`:
|
||||
|
||||
- `openwork-mac-arm64-<version>.dmg`
|
||||
- `openwork-mac-x64-<version>.dmg`
|
||||
- `openwork-win-x64-<version>.exe`
|
||||
- `openwork-linux-x86_64-<version>.AppImage`
|
||||
- `openwork-linux-arm64-<version>.AppImage`
|
||||
|
||||
There is no first-request GitHub download inside Den, artifact lookup API call,
|
||||
ZIP creation, per-pod cold cache, or different repeated-download path. Every
|
||||
internet-connected request redirects immediately. Every mounted request streams
|
||||
the same provisioned file. With more than one Den API replica, mount the same
|
||||
read-only PVC on every replica.
|
||||
|
||||
## Default connection handoff (no key)
|
||||
|
||||
`DEN_CONNECT_LINK_MODE=exchange` is the default. When the user clicks **Open
|
||||
OpenWork**, Den mints a fresh five-minute bearer code and stores only its
|
||||
SHA-256 hash. The app:
|
||||
|
||||
1. posts the code back to the exact HTTPS API origin carried in the deep link
|
||||
to preview the organization and server;
|
||||
2. shows an explicit confirmation without changing local configuration; and
|
||||
3. posts the code to the exchange endpoint after confirmation, consuming it
|
||||
exactly once before writing `desktop-bootstrap.json`.
|
||||
|
||||
The grant is stored in MySQL, not pod memory, so preview and consumption can
|
||||
land on different Kubernetes replicas. A conditional database update makes
|
||||
concurrent exchange attempts single-use. Expired rows are removed while new
|
||||
grants are minted.
|
||||
|
||||
The web page fetches a new code at the moment the user clicks **Open**, rather
|
||||
than relying on the code loaded before a potentially long installer run. If a
|
||||
code expires or was already used, the user can return to the same Den page and
|
||||
click **Open** again.
|
||||
|
||||
## Optional signed handoff
|
||||
|
||||
Signed handoffs remain an explicit upgrade path. They are disabled unless all
|
||||
of the following are true:
|
||||
|
||||
- `DEN_CONNECT_LINK_MODE=signed`;
|
||||
- `DEN_CONNECT_LINK_KEY_ID` is configured;
|
||||
- `DEN_CONNECT_LINK_PRIVATE_KEY` contains the dedicated Ed25519 private key;
|
||||
- the matching public key is already embedded in the desktop build.
|
||||
|
||||
For Helm:
|
||||
|
||||
```yaml
|
||||
config:
|
||||
public:
|
||||
connectLinkMode: signed
|
||||
connectLinkKeyId: "owc-2026-07"
|
||||
|
||||
secret:
|
||||
values:
|
||||
connectLinkPrivateKey: |-
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
...
|
||||
-----END PRIVATE KEY-----
|
||||
```
|
||||
|
||||
Generate a pair with `scripts/generate-connect-link-keypair.mjs`. Do not enable
|
||||
signed mode until the production release embeds the corresponding public key.
|
||||
This optional mode changes only the configuration handoff; it still downloads
|
||||
the same standard installer through the same direct or mounted route.
|
||||
|
||||
## MDM alternative
|
||||
|
||||
Customer-facing guidance for this path is published at
|
||||
[`packages/docs/start-here/enterprise-desktop-deployment.mdx`](../packages/docs/start-here/enterprise-desktop-deployment.mdx).
|
||||
|
||||
Managed deployments can skip the deep-link handoff by deploying a standard
|
||||
binary — typically the enterprise distribution
|
||||
(`openwork-enterprise-<os>-<arch>-<version>.<ext>`, published on the
|
||||
`enterprise` release channel), or the public installer — and writing
|
||||
`desktop-bootstrap.json` directly:
|
||||
|
||||
| OS | Canonical path |
|
||||
|---|---|
|
||||
| Windows | `%LOCALAPPDATA%\openwork\desktop-bootstrap.json` (`%XDG_CONFIG_HOME%\openwork\desktop-bootstrap.json` wins if set) |
|
||||
| macOS/Linux | `$XDG_CONFIG_HOME/openwork/desktop-bootstrap.json`, falling back to `~/.config/openwork/desktop-bootstrap.json` |
|
||||
|
||||
```json
|
||||
{
|
||||
"baseUrl": "https://openwork.example.com",
|
||||
"apiBaseUrl": "https://api.openwork.example.com",
|
||||
"requireSignin": true,
|
||||
"writtenAt": "2026-07-14T12:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
Current builds still read the older `~/.config/openwork` path for
|
||||
compatibility. When both files exist, the valid configuration with the newest
|
||||
`writtenAt` wins.
|
||||
|
||||
## Security properties
|
||||
|
||||
- Install-link tokens and exchange codes are stored only as SHA-256 hashes.
|
||||
- Connection codes expire after five minutes and are consumed exactly once in
|
||||
MySQL across all Den API replicas.
|
||||
- The desktop requires HTTPS outside explicit development loopback mode and
|
||||
refuses redirects during preview and exchange.
|
||||
- Returned claims must identify the same API origin carried by the deep link.
|
||||
- The app displays the exact organization and server before changing local
|
||||
configuration; cancel and every validation failure leave it untouched.
|
||||
- Branding URLs from the keyless exchange are not loaded before confirmation.
|
||||
- The standard release installer bytes are never modified, preserving normal
|
||||
macOS and Windows signature verification.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Fix |
|
||||
|---|---|
|
||||
| Download redirects to GitHub | Expected for internet-connected deployments. Allow `github.com` and its release-asset redirect/CDN host on the user's network. |
|
||||
| Download returns `404` | The install token is invalid, expired, or revoked. Ask an org member for the current install page link. |
|
||||
| Mounted download fails | Verify the filename exactly matches the configured release tag and every Den replica mounts the same readable PVC path. |
|
||||
| **Open OpenWork** cannot prepare a connection | Verify the browser can reach Den API and `DEN_API_PUBLIC_URL` is the correct public HTTPS origin. |
|
||||
| The app refuses an expired or used link | Return to the same Den install page and click **Open OpenWork** again to mint a fresh code. |
|
||||
| Signed mode reports an unknown key | Return to `exchange` mode, or ship a desktop build containing the matching public key before re-enabling `signed`. |
|
||||
| Install links point at the wrong web host | Correct `BETTER_AUTH_URL` and `DEN_BETTER_AUTH_TRUSTED_ORIGINS`, then restart Den. |
|
||||
Reference in New Issue
Block a user