Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [Unreleased]

### Added

- **A driver's trace size limit is in the tree.** `DirXML-TraceSizeLimit` is driver configuration, so
`import-live`, a Designer project and a Designer export now record it as `trace-size-limit` in
`driver.xml` (absent when the driver inherits the driver set's). A new driver is created with it. An
existing driver keeps the vault's value unless `vault.deploy --trace-size-limit` is given; without the
flag the plan notes the difference. The change never restarts a driver, because the engine reads trace
settings live, and it never counts as drift. A tree that doesn't record a limit asks for nothing, so
older trees diff as before. Also: `driver.set --key trace-size-limit`, the override key
`drivers/<driver>.trace-size-limit`, and `vault.compare-envs` values.
- **A new driver starts with its trace level and file.** A deploy that creates a driver writes the
`DirXML-TraceLevel` and `DirXML-TraceFile` the tree recorded. On an existing driver they stay operational
(`driver.trace`), and are never compared or written. Designer projects and exports now read and write the
driver's own trace level and file too.

### Changed

- A deploy whose plan is empty but whose diff isn't (a difference the plan leaves alone and notes) now
ends with "nothing to deploy — the plan leaves the differences above as the notes say" instead of
claiming the vault matches the tree.

## [0.63.0] - 2026-11-02

### Fixed
Expand Down
9 changes: 5 additions & 4 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ Deploys the tree to a vault in this order: validate, diff, plan, snapshot, write
```
bin/idm vault.deploy <tree> --env <name> [--driver <name>…] --dry-run|--yes|--step [--confirm <name>]
[--no-restart] [--secrets none|missing|all] [--allow-missing-secrets]
[--capture-drift] [--capture-only] [--base-branch <branch>]
[--capture-drift] [--capture-only] [--base-branch <branch>] [--trace-size-limit]
[--driver-server <driver>=<serverDn>…] [--delete-driver <name>…] [--delete-all <kind>…] [--json]
```

Expand All @@ -642,6 +642,7 @@ bin/idm vault.deploy <tree> --env <name> [--driver <name>…] --dry-run|--yes|--
| `--capture-drift` | no | when a `prd` vault differs from the last deploy on record, record its state as a commit on an `as-found/<env>/<time>` branch. The deploy is still refused: deploy again on top of it |
| `--capture-only` | no | read the vault and capture its state only (`captured`, `verified` or `known`); no plan, no write, no `--yes` or `--confirm` needed. The tree must be in git |
| `--base-branch` | no | a branch name; a `prd` deploy is refused unless the tree is on it |
| `--trace-size-limit` | no | also write the tree's trace size limit (`trace-size-limit` in `driver.xml`) over an existing driver's. Default: a new driver gets the tree's, an existing driver keeps the vault's and the plan notes the difference. Never restarts a driver |
| `--driver-server` | no, repeatable | `<driver>=<serverDn>`: the server a driver new to this environment runs on, when it is not the server the deploy connects through |
| `--delete-driver` | no, repeatable | a driver in the vault but not in the tree, to delete with its subtree. It must be stopped; refused when it is still in the tree or not in the vault |
| `--delete-all` | no, repeatable (one kind each) | allow deletes the mass-deletion guard would hold back for a kind: `entitlements`, `jobs`, `rbe-policies`, `templates`, `forms`, `prds`, `entities`, `choices`, `relationships`, `dm-configs`, `roles`, `resources`, `attestations`, `sods`, `role-configs`, `reports`, `nav-items`, `auth-types`, `web-app-configs`, `containers`, `others`. Another value is refused |
Expand Down Expand Up @@ -2692,7 +2693,7 @@ bin/idm driver.server.copy tree --from primary --to idm2 --driver "Active Direct

### `driver.set`

Set a driver setting: shim-class, shim-auth-server, shim-auth-id, environments (the environments it is deployed to, comma-separated; --clear = every environment), planned (environments it is meant to reach but isn't promoted to yet), param:&lt;shim parameter&gt;, engine:&lt;engine control value&gt;.
Set a driver setting: shim-class, shim-auth-server, shim-auth-id, trace-size-limit (the most its trace file grows to, as the vault holds it; --clear = the tree stops recording it and the vault keeps its own), environments (the environments it is deployed to, comma-separated; --clear = every environment), planned (environments it is meant to reach but isn't promoted to yet), param:&lt;shim parameter&gt;, engine:&lt;engine control value&gt;.

```
bin/idm driver.set <tree> --driver <driver> --key <key> [--value <value>] [--clear] [--dry-run] [--force] [--json]
Expand All @@ -2702,7 +2703,7 @@ bin/idm driver.set <tree> --driver <driver> --key <key> [--value <value>] [--cle
|---|---|---|
| `<tree>` | yes | the IDM-as-code tree to change |
| `--driver` | yes | driver name |
| `--key` | yes | shim-class\|shim-auth-server\|shim-auth-id\|environments\|planned\|param:&lt;name&gt;\|engine:&lt;name&gt; |
| `--key` | yes | shim-class\|shim-auth-server\|shim-auth-id\|trace-size-limit\|environments\|planned\|param:&lt;name&gt;\|engine:&lt;name&gt; |
| `--value` | no | the value |
| `--clear` | no, a flag | make the setting empty (an empty --value reads as missing on the command line) |

Expand Down Expand Up @@ -2792,7 +2793,7 @@ bin/idm override.set <tree> --env <env> --key <key> --value <value> [--dry-run]
|---|---|---|
| `<tree>` | yes | the IDM-as-code tree to change |
| `--env` | yes | environment name |
| `--key` | yes | drivers/&lt;driver&gt;.gcv.&lt;name&gt; \| .shim.&lt;name&gt; \| .ecv.&lt;name&gt; \| .shim-auth-server \| .shim-auth-id \| .name (the driver's name in that vault) \| driverset.gcv.&lt;name&gt; |
| `--key` | yes | drivers/&lt;driver&gt;.gcv.&lt;name&gt; \| .shim.&lt;name&gt; \| .ecv.&lt;name&gt; \| .shim-auth-server \| .shim-auth-id \| .trace-size-limit \| .name (the driver's name in that vault) \| driverset.gcv.&lt;name&gt; |
| `--value` | yes | the environment's value |

```bash
Expand Down
3 changes: 2 additions & 1 deletion docs/console-gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ log entries kept (0 turns the set's logging off), `DirXML-LogEventsType` the pag
without its own values uses the driver set's; `--inherit` clears a driver's four. The command shows all of
this for a driver or the set and sets it live; `--level specific-events` needs `--events`. The trace
settings were never in the tree (they are operational, `driver.trace`), and the log level follows them
rather than the note's earlier idea of a tree attribute.
rather than the note's earlier idea of a tree attribute. *(2026-11-02: the trace size limit is the
exception — driver configuration, recorded in the tree; see vault-deploy.md, Decisions 6.)*

**G11 `driver.secrets set|remove --kind named|shim-auth|remote-loader|key|keystore`.** The engine's
`SetRemoteLoaderPassword`, `SetMutualAuthKeyPassword` and `SetMutualAuthKSPassword` (and their Clear
Expand Down
6 changes: 4 additions & 2 deletions docs/tree-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ servers".
Values that differ per stage — one file per environment, `key = value`
lines for GCVs, shim parameters, engine control values and the driver's
connection settings (`drivers/<driver>.gcv.<name>`, `.ecv.<name>`,
`.shim-auth-server`, `.shim-auth-id`,
`.shim-auth-server`, `.shim-auth-id`, `.trace-size-limit`,
`drivers/<driver>.shim.<name>`, `driverset.gcv.<name>`). The tree's own
files keep the base value; a deploy to that environment applies its file. See
[getting-started.md](getting-started.md) §3.1.
Expand Down Expand Up @@ -147,7 +147,9 @@ doesn't have.
### `drivers/<driver>/driver.xml`

That driver's manifest: shim class, authentication server and id (not the
password), which environments get it (`environments="QA, PROD"`, absent = every
password), its trace size limit (`trace-size-limit`, the vault's
`DirXML-TraceSizeLimit`; absent when it inherits the driver set's), the trace level and file
it was read with (`dirxml-tracelevel` / `dirxml-tracefile` meta: a new driver starts with them), which environments get it (`environments="QA, PROD"`, absent = every
environment) and which it is planned for but not promoted to yet
(`planned="PROD"`), which config files exist, every policy and resource, the
entitlements, and the **linkage** — which object runs in which policy set, in
Expand Down
12 changes: 11 additions & 1 deletion docs/vault-deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ Since 0.46.0 (DirXMLDev-web #136):
`key = value` line per value that differs from the tree's base. Keys:
`drivers/<driver>.gcv.<name>`, `drivers/<driver>.shim.<name>`,
`drivers/<driver>.ecv.<name>`, `drivers/<driver>.shim-auth-server`,
`drivers/<driver>.shim-auth-id`, `driverset.gcv.<name>`
`drivers/<driver>.shim-auth-id`, `drivers/<driver>.trace-size-limit`, `driverset.gcv.<name>`
(docs/getting-started.md §3.1). Precedence for a shim auth id: the secrets
file's `<driver>.shim-auth-id` beats the override, which beats the tree. Where
it acts:
Expand Down Expand Up @@ -665,6 +665,16 @@ discipline), 5 and 7 are not.
never touched on updates unless **forced** (`--secrets all|missing`,
`vault.secrets --set`), verified by name where the vault allows, never
printed, snapshotted or logged.
6. ✅ **Confirmed 2026-11-02 (Jerry): the trace size limit is driver configuration.**
The tree records a driver's `DirXML-TraceSizeLimit` (`trace-size-limit` in
`driver.xml`). A new driver is created with it. Overwriting an existing driver's
value is optional: without `--trace-size-limit` the vault keeps its own and the
plan says so; with it, the plan writes the tree's. Either way no restart (the
engine reads trace settings live), and a difference is never drift. A tree that
doesn't record a limit asks for nothing. A new driver also starts with the trace
level and file the tree saw (`dirxml-tracelevel` / `dirxml-tracefile` in
`driver.xml`); on an existing driver those stay operational (`driver.trace`),
never compared or written.

## Incident 2026-09-16 — a stale tree emptied a kind (recovered from the snapshot)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ private static Driver readDriver(Path dir) throws IOException {
d.shimClass = attr(m, "shim-class");
d.shimAuthServer = attr(m, "shim-auth-server");
d.shimAuthId = attr(m, "shim-auth-id");
d.traceSizeLimit = attr(m, Driver.TRACE_SIZE_LIMIT);
d.environments.addAll(com.pointblue.dirxml.dev.model.EnvironmentList.parse(attr(m, "environments")));
d.planned.addAll(com.pointblue.dirxml.dev.model.EnvironmentList.parse(attr(m, "planned")));
readMeta(m, d.meta);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ private static void writeDriver(Driver d, Path dir) throws IOException {
Manifest m = new Manifest("driver");
m.attr("name", d.name).attr("dn", d.dn).attr("shim-class", d.shimClass)
.attr("shim-auth-server", d.shimAuthServer).attr("shim-auth-id", d.shimAuthId)
.attr(Driver.TRACE_SIZE_LIMIT, d.traceSizeLimit)
.attr("environments", com.pointblue.dirxml.dev.model.EnvironmentList.format(d.environments))
.attr("planned", com.pointblue.dirxml.dev.model.EnvironmentList.format(d.planned));
m.meta(d.meta);
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/pointblue/dirxml/dev/deploy/DeployCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* vault.diff <tree> --env <name> [--driver D…] [--json]
* vault.deploy <tree> --env <name> [--driver D…] [--dry-run | --yes | --step] [--confirm <name>]
* [--no-restart] [--secrets none|missing|all] [--allow-missing-secrets] [--capture-drift] [--capture-only] [--base-branch B] [--json]
* [--delete-driver D…] [--delete-all entitlements|jobs|rbe-policies|templates|forms|prds|roles|entities|… …]
* [--trace-size-limit] [--delete-driver D…] [--delete-all entitlements|jobs|rbe-policies|templates|forms|prds|roles|entities|… …]
* vault.verify <tree> --env <name> [--driver D…] [--json]
* vault.rollback --env <name> --snapshot <file> [--yes] [--json]
* vault.secrets --env <name> --driver D --set <key> …
Expand All @@ -31,7 +31,7 @@ private DeployCli() {
Map.entry("vault.diff", Args.Spec.of("", "env driver")),
Map.entry("vault.verify", Args.Spec.of("", "env driver")),
Map.entry("vault.compare-envs", Args.Spec.of("", "env with")),
Map.entry("vault.deploy", Args.Spec.of("dry-run yes step no-restart allow-missing-secrets capture-drift capture-only",
Map.entry("vault.deploy", Args.Spec.of("dry-run yes step no-restart allow-missing-secrets capture-drift capture-only trace-size-limit",
"env driver confirm secrets base-branch driver-server delete-driver delete-all")),
Map.entry("vault.rollback", Args.Spec.of("yes", "env snapshot confirm server")),
Map.entry("vault.schema", Args.Spec.of("", "env")),
Expand Down Expand Up @@ -140,6 +140,7 @@ public static int run(String[] argv) throws Exception {
o.json = json;
o.deleteDrivers = opts.getOrDefault("delete-driver", List.of());
o.deleteAllKinds = opts.getOrDefault("delete-all", List.of());
o.traceSizeLimit = opts.containsKey("trace-size-limit");
Deployer.Result r = new Deployer(o).run();
System.out.print(json ? r.json() + "\n" : r.text());
return r.ok ? 0 : 1;
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/com/pointblue/dirxml/dev/deploy/Deployer.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public static final class Options {
public Map<String, String> driverServers = Map.of();
/** {@code --delete-all} kinds ({@code entitlements}, {@code forms}, {@code prds}), repeatable. */
public List<String> deleteAllKinds = List.of();
/** {@code --trace-size-limit}: write the tree's trace size limit over an existing driver's (a new driver always gets it). */
public boolean traceSizeLimit;
public int restartWaitSeconds = 180;
/**
* Hears each step as it happens, one line each ("snapshot: …", "write 3/9: modify cn=…", "restarting AD: waiting
Expand Down Expand Up @@ -325,7 +327,7 @@ public Result run() throws IOException {
}
}
}
Plan plan = Plan.of(diff, to, dsDn, secrets, o.secretsMode, liveNamed, o.restart, o.tree, o.deleteDrivers, vault, o.deleteAllKinds, scope);
Plan plan = Plan.of(diff, to, dsDn, secrets, o.secretsMode, liveNamed, o.restart, o.tree, o.deleteDrivers, vault, o.deleteAllKinds, scope, o.traceSizeLimit);
r.planText = validationNote + overrideNote + plan.text(env.name, dsDn);
o.progress.accept("planned: " + plan.steps.size() + " write(s), " + (plan.restart.size() + plan.serverRestarts.size()) + " restart(s)");
if (!plan.deleteDriverRefusals.isEmpty()) {
Expand All @@ -334,7 +336,9 @@ public Result run() throws IOException {
}
if (plan.isEmpty()) {
r.ok = true;
r.planText += "nothing to deploy — the vault matches the tree\n";
// a difference the plan leaves alone on purpose (its note says why) is not a match
r.planText += diff.isEmpty() ? "nothing to deploy — the vault matches the tree\n"
: "nothing to deploy — the plan leaves the differences above as the notes say\n";
return r;
}
if (!plan.missingSecrets.isEmpty() && !o.allowMissingSecrets) {
Expand Down Expand Up @@ -531,7 +535,9 @@ private static ReadApplied readApplied(Path tree, Environments.Environment env)
/** The tree (as {@code env} gets it) against the live vault, what the tree doesn't deploy there set aside either way. */
private static ModelDiff driftOf(Path tree, Environments.Environment env, DriverSet live) throws IOException {
ReadApplied r = readApplied(tree, env);
return r.applied().ignoreNotDeployed(ModelDiff.of(r.ds(), live));
// the vault's trace size limit is its own unless a deploy is told otherwise (--trace-size-limit): never drift
return r.applied().ignoreNotDeployed(ModelDiff.of(r.ds(), live))
.only(ch -> !(ch.kind == ModelDiff.Kind.DRIVER_SETTING && com.pointblue.dirxml.dev.model.Driver.TRACE_SIZE_LIMIT.equals(ch.what)));
}

/** Execute one step; records it in the result; throws on failure. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* <ul>
* <li><b>values</b>: every scalar value an override can carry ({@code drivers/<d>.gcv.<n>} for the
* driver's own GCVs, {@code .shim.<n>}, {@code .ecv.<n>}, {@code .shim-auth-server},
* {@code .shim-auth-id}, {@code driverset.gcv.<n>}) that both hold and that differs, keyed as the
* {@code .shim-auth-id}, {@code .trace-size-limit}, {@code driverset.gcv.<n>}) that both hold and that differs, keyed as the
* overrides file keys it;</li>
* <li><b>drift</b>: the diff of the first against the second after the first takes the second's value
* for each of those, so what remains is what a value can't explain: policies, filters, linkage,
Expand Down Expand Up @@ -484,6 +484,7 @@ public static Map<String, String> values(DriverSet ds) {
definitions(d.config.get(Driver.ENGINE_CONTROL_VALUES), p + "ecv.", out);
out.put(p + Driver.SHIM_AUTH_SERVER, d.shimAuthServer);
out.put(p + Driver.SHIM_AUTH_ID, d.shimAuthId);
out.put(p + Driver.TRACE_SIZE_LIMIT, d.traceSizeLimit);
}
return out;
}
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/com/pointblue/dirxml/dev/deploy/ModelDiff.java
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,9 @@ public List<String> affectedDrivers() {
return new ArrayList<>(affected);
}
for (Change c : changes) {
if (c.driver != null && c.kind != Kind.DRIVER_ADDED && c.kind != Kind.DRIVER_REMOVED && !c.kind.noRestart()) {
// the engine reads a driver's trace settings live, as driver.trace set relies on: no restart for the size limit
boolean traceSize = c.kind == Kind.DRIVER_SETTING && Driver.TRACE_SIZE_LIMIT.equals(c.what);
if (c.driver != null && c.kind != Kind.DRIVER_ADDED && c.kind != Kind.DRIVER_REMOVED && !c.kind.noRestart() && !traceSize) {
affected.add(c.driver);
}
}
Expand Down Expand Up @@ -1207,6 +1209,10 @@ private static String describeKind(Artifact a) {
private void diffDriverSettings(Driver a, Driver b) {
settingChange(a, "shim-class", a.shimClass, b.shimClass);
settingChange(a, "shim-auth-server", a.shimAuthServer, b.shimAuthServer);
if (b.traceSizeLimit != null) {
// a side that never recorded the limit (an older tree) asks for nothing
settingChange(a, Driver.TRACE_SIZE_LIMIT, a.traceSizeLimit, b.traceSizeLimit);
}
if (a.shimAuthIdSecret || b.shimAuthIdSecret) {
// the secrets file supplies the id: say that it differs, never what either side holds
if (!Objects.equals(a.shimAuthId, b.shimAuthId)) {
Expand Down
Loading
Loading