mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-29 16:15:51 +00:00
refactor: derive an app's address from its principal instead of storing it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYWxvwmteVyzcWenJfbnzs
This commit is contained in:
co-authored by
Claude Opus 5
parent
49f8a253fd
commit
673e3f26e7
@@ -1,47 +1,31 @@
|
||||
# Removing `policy.on_behalf_of_email`
|
||||
# `policy.on_behalf_of_email`
|
||||
|
||||
An app's identity is `policy.on_behalf_of`; the address beside it is a function of that
|
||||
principal. `on_behalf_of_email` is no longer required — a policy carrying only a principal
|
||||
executes, deriving the address — but it is still written on every save, and that is the only
|
||||
thing holding it in place.
|
||||
principal. The policy stores only the principal. The address is derived wherever it is needed:
|
||||
at execution (`get_on_behalf_of`, `$ctx:author`) and on the reads that return a deployed policy
|
||||
(`attach_on_behalf_of_email`), where it is a response-only field the write path ignores.
|
||||
|
||||
## The gate
|
||||
|
||||
**This cannot ship until `MIN_KEEP_ALIVE_VERSION` (`windmill-common/src/min_version.rs`) has
|
||||
passed 1.810.**
|
||||
|
||||
`get_on_behalf_of` gained its derive-when-absent fallback in **1.810**. Every replica before that
|
||||
*requires* the key and errors outright without it, so it would 400 every anonymous, publisher and
|
||||
guest app saved by a newer one. A rolling deploy runs both versions at once, which is why the
|
||||
write stays until no replica older than 1.810 can be live — in practice, once
|
||||
`MIN_KEEP_ALIVE_VERSION` (`windmill-common/src/min_version.rs`) has passed it.
|
||||
*requires* the key and errors outright without it, so a rolling deploy that runs both would 400
|
||||
every anonymous, publisher and guest app the moment one of them saves. The write is what holds
|
||||
the key in place, so stopping it is only safe once no replica older than 1.810 can be live.
|
||||
|
||||
There is no `MIN_VERSION_*` constant for this and it does not need one: those exist to gate
|
||||
behavior at runtime or to trip the build when a constraint expires, and nothing here does either.
|
||||
The key is written unconditionally, so no replica ever meets its absence until someone follows
|
||||
the steps below.
|
||||
`MIN_KEEP_ALIVE_VERSION` is 1.420.0, and a compile-time rule keeps it at least 50 minor versions
|
||||
behind current, so it cannot reach 1.810 until releases pass ~1.860.
|
||||
|
||||
## Step 1 — stop writing the key
|
||||
There is no `MIN_VERSION_*` constant for this and it does not need one: those gate behavior at
|
||||
runtime or trip the build when a constraint expires, and nothing here does either.
|
||||
|
||||
- `stored_on_behalf_of_email` in `windmill-api/src/apps.rs`, and the `create_app` /
|
||||
`update_app_internal` call sites that store what it returns.
|
||||
- The CLI and frontend workspace-deploy paths (`cli/windmill-utils-internal/src/deploy.ts`,
|
||||
`frontend/src/lib/utils_workspace_deploy.ts`). These send the address *instead of* a principal
|
||||
for a cross-workspace deploy, which is the one case where it is the only identity available —
|
||||
so this is "stop sending it once the target resolves a principal itself", not a deletion.
|
||||
## Why the address is not derived on a draft read
|
||||
|
||||
Policies written before this keep their key and keep being read from it; they agree with their
|
||||
principal, so nothing has to strip them.
|
||||
|
||||
## Step 2 — drop the field
|
||||
|
||||
Remove `on_behalf_of_email` from `Policy` and the fallback in `get_on_behalf_of`, which then
|
||||
always derives. Optionally strip the key from stored policies.
|
||||
|
||||
This can ship with step 1. It is written separately because step 1 alone is revertible without
|
||||
touching stored data or the response schema, and because the gate above is what makes either
|
||||
step safe — nothing about step 2 needs its own waiting period.
|
||||
|
||||
## Why the address is not derived on read
|
||||
|
||||
Read paths return the stored address verbatim rather than recomputing it. Deriving on read means
|
||||
resolving a principal that, for a draft, is caller-controlled — which turns the read into an
|
||||
oracle for addresses the caller cannot otherwise see, and leaves a principal that resolves to
|
||||
nobody with no address at all. Both were live defects while the read paths did derive.
|
||||
A deployed policy's principal was written by the server, from an identity `resolve_on_behalf_of`
|
||||
validated. A draft's is whatever its editor last typed. Deriving one would answer "which address
|
||||
is `u/x`?" for any principal a caller cares to name — including a superadmin outside their own
|
||||
workspaces, whom `resolve_username_to_email` reaches instance-wide. That was a live defect while
|
||||
every read derived, and is why the derivation is attached per read path rather than to `Policy`
|
||||
itself.
|
||||
|
||||
Reference in New Issue
Block a user