Lesson 05 / 11
Graphical Interface and Command Line
Counting the trace the same ten management jobs leave in two management forms: the graphical interface and the command line tying at 11 items appearing in the audit trail, three jobs leaving no trace at all in either form, the reproducible job count needing a script and a repository to rise from 0 to 10, and manual operations across forty machines dropping from 400 to 10.
Published Updated
In this lesson
- Writes the trace a management job leaves with three numbers: records dropped, items appearing in the audit trail, reproducibility
- Distinguishes whether the audit trail comes from the form or from the subsystem keeping its own record
- Computes the trace and operation cost of repeating the same job across many machines
Contents
The previous lesson measured the expressive power of permission models and left one question open: a permission line changing is also an event. Policy changes when ownership is compromised or when an admin changes a rule; what that change leaves behind is something just as measurable as the permission line itself.
This lesson performs the same management jobs in two forms — through a graphical interface and through the command line — and counts the trace items each one leaves behind.
A Trace Is a Number
When a management job finishes, what is left behind is written with three separate measures.
How many records fall. At the end of the job, how many separate places a trace forms in: the subsystem’s own record, the interface’s own log, the shell history, a script file, a repository commit, a change ticket.
How many appear in the audit trail. Not all records are equal. If a record can be deleted by the person who did the job, it does not count as an audit trail item. Shell history is the user’s own file; the interface’s local log rotates and is not retained. A record that is not persistent is not a trace — it is a reminder.
How many can be reproduced. If a record carries exactly what the change was, the same change can be repeated identically on another machine or rolled back. A record saying “a user was added” is not the same thing as a record carrying the definition of that addition.
Ten Jobs, Three Forms
The example is ten management jobs performed on the branch servers of the fictional regional measurement network. Both the jobs and the record scheme are fictional; the table below is a model — there is no real interface, shell, or product.
HO23: the management job set is 10, and each job can be done in three forms. HO24: the
record type is 6; the persistent field says the record cannot be deleted by the person who did
the job and appears in the audit trail, the reproduces field says the record is enough to
reproduce the change. HO25: the system record and the change ticket are
form-independent — if the subsystem doing the job keeps its own record, it keeps it in all
three forms. HO26: a form only adds its own extra records. HO27: the same job is repeated
across forty machines; in the script-and-repo form, the definition is written once and run forty
times.
// it/trace.mjs — the trace the same management job leaves in two forms (model): fictional // measurement network, no real interface, shell, or product. No randomness. // Record types. persistent: cannot be deleted by the user, appears in the audit trail. // reproduces: the record itself is enough to reproduce the change. export const TYPE = { "system-record": { persistent: true, reproduces: false }, "change-ticket": { persistent: true, reproduces: false }, "interface-log": { persistent: false, reproduces: false }, "shell-history": { persistent: false, reproduces: false }, "script-file": { persistent: false, reproduces: true }, "repo-commit": { persistent: true, reproduces: true }, }; // Records each form adds to every job. The system record and the change ticket are form-independent. export const FORM = { graphical: ["interface-log"], command: ["shell-history"], "script-repo": ["shell-history", "script-file", "repo-commit"] }; // system: does the subsystem doing the job keep its own record. ticket: is the job tied to a change ticket. export const JOB = ` add-user yes yes change-permission no yes stop-service yes yes firewall-rule yes yes mount-disk-volume no no install-software yes yes set-clock yes no install-certificate no no take-backup yes no review-log no no` .trim().split("\n").map((r) => { const [name, system, ticket] = r.trim().split(/\s+/); return { name, system: system === "yes", ticket: ticket === "yes" }; }); export const records = (job, form) => [...(job.system ? ["system-record"] : []), ...(job.ticket ? ["change-ticket"] : []), ...FORM[form]]; export function measure(form) { const r = JOB.map((job) => records(job, form)); const persistent = r.map((x) => x.filter((t) => TYPE[t].persistent).length); return { records: r.reduce((a, x) => a + x.length, 0), inAudit: persistent.reduce((a, x) => a + x, 0), notInAudit: r.reduce((a, x) => a + x.filter((t) => !TYPE[t].persistent).length, 0), traceless: persistent.filter((x) => x === 0).length, reproducible: r.filter((x) => x.some((t) => TYPE[t].reproduces && TYPE[t].persistent)).length }; }
// it/tracemeasure.mjs — how many records fall, how many appear in the audit trail, how many can be reproduced. import { TYPE, FORM, JOB, records, measure } from "./trace.mjs"; const write = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); const B = Object.keys(FORM); const MACHINES = 40; console.log(`${JOB.length} management jobs x ${B.length} forms; record types ${Object.keys(TYPE).length}: ` + Object.entries(TYPE).map(([t, v]) => `${t}${v.persistent ? " (persistent)" : ""}`).join(", ")); const A = [-24, -8, -8, 11, 10, 13]; console.log("\n1. record items per job (in parentheses: items appearing in the audit trail)"); write(A, "job", "system", "ticket", ...B); for (const job of JOB) { const h = (b) => { const r = records(job, b); return `${r.length} (${r.filter((t) => TYPE[t].persistent).length})`; }; write(A, job.name, job.system ? "yes" : "no", job.ticket ? "yes" : "no", ...B.map(h)); } const C = [-40, 11, 10, 13]; console.log("\n2. total per form"); write(C, "measure", ...B); const R = Object.fromEntries(B.map((b) => [b, measure(b)])); for (const [name, f] of [["record items dropped", (r) => r.records], ["items appearing in the audit trail", (r) => r.inAudit], ["items not appearing in the audit trail", (r) => r.notInAudit], ["jobs leaving no audit trail", (r) => `${r.traceless}/${JOB.length}`], ["reproducible jobs", (r) => `${r.reproducible}/${JOB.length}`]]) write(C, name, ...B.map((b) => f(R[b]))); const D = [-40, 11, 10, 13]; console.log(`\n3. the same job repeated across ${MACHINES} machines`); write(D, "measure", ...B); for (const [name, f] of [["manual operations", (b) => (b === "script-repo" ? JOB.length : JOB.length * MACHINES)], ["place storing the change definition", (b) => (b === "script-repo" ? 1 : 0)], ["items appearing in the audit trail", (b) => R[b].inAudit * MACHINES]]) write(D, name, ...B.map(f));
10 management jobs x 3 forms; record types 6: system-record (persistent), change-ticket (persistent), interface-log, shell-history, script-file, repo-commit (persistent) 1. record items per job (in parentheses: items appearing in the audit trail) job system ticket graphical command script-repo add-user yes yes 3 (2) 3 (2) 5 (3) change-permission no yes 2 (1) 2 (1) 4 (2) stop-service yes yes 3 (2) 3 (2) 5 (3) firewall-rule yes yes 3 (2) 3 (2) 5 (3) mount-disk-volume no no 1 (0) 1 (0) 3 (1) install-software yes yes 3 (2) 3 (2) 5 (3) set-clock yes no 2 (1) 2 (1) 4 (2) install-certificate no no 1 (0) 1 (0) 3 (1) take-backup yes no 2 (1) 2 (1) 4 (2) review-log no no 1 (0) 1 (0) 3 (1) 2. total per form measure graphical command script-repo record items dropped 21 21 41 items appearing in the audit trail 11 11 21 items not appearing in the audit trail 10 10 20 jobs leaving no audit trail 3/10 3/10 0/10 reproducible jobs 0/10 0/10 10/10 3. the same job repeated across 40 machines measure graphical command script-repo manual operations 400 400 10 place storing the change definition 0 0 1 items appearing in the audit trail 440 440 840
The numbers belong to the measurement class; their inputs are HO23–HO27.
Two Forms Leave the Same Trace
The first three rows of the second table give an unexpected equality. The graphical interface and the command line produce the same numbers on the same ten jobs: 21 record items, of which 11 appear in the audit trail and 10 do not. The fourth row is the same too: in both forms, 3 jobs leave no audit trail at all.
The reason is in the first table. The items that appear in the audit trail come from only two types: the subsystem’s own record and the change ticket. Both are form-independent. Doing a job through the graphical interface does not turn a job the subsystem does not record into a recorded one; doing the same job through the command does not either.
The item each form adds — the interface’s local log and the shell history — is not persistent. Both can be deleted by the person who did the job, and neither carries the definition of the change. This is why neither counts as an audit trail item; they show up as 10 in the columns, and that 10 contributes nothing to the measure.
“It was done from the command line, so there is a record” is an assumption, and it is wrong in the model. Shell history is a file in the user’s home directory: it can be turned off, deleted, edited. An audit trail item sits in a place that is not under the control of the person who left the trace.
The Difference Is in Reproducibility
The fifth row separates the two forms for the first time — but what it separates is not graphical from command, it is scripted from unscripted. The number of reproducible jobs is 0/10 in the graphical interface, also 0/10 for a manually typed command, and 10/10 in the script-and-repo form.
What separates a manually typed command from a script is that its definition sits nowhere. The command runs, leaves its result, and disappears itself. Shell history keeps the command lines, but a history is not a definition: it does not write down the order, the context, which machine it ran on, or which attempt failed.
In the script-and-repo form, two items are added, and the two work together. The script file
reproduces the change but is not itself persistent — it sits on disk and can be altered. The
repository commit is both persistent and carries the definition. This is why the measure looks
for the reproduces and persistent fields together: reproducibility does not depend on a
definition existing — it depends on the definition sitting in a place that cannot be altered.
The same row also hides a cost. In the script-and-repo form, the records dropped rise to 41 and the items not appearing in the audit trail rise to 20. This item not appearing in the audit trail doubling is not a loss — the items added are already accounted for in the repository commit — but it raises the number of places to look. In an incident, three separate record sets are compared, and that comparison is itself a job.
Three Jobs Leave No Trace
The 3/10 in the fourth row is the same in both forms and is the measurement’s most direct output. Behind mounting a disk volume, installing a certificate, and reviewing a log there is neither a subsystem record nor a change ticket; when these three jobs are done, only a deletable item is left behind.
What these three jobs share is that two of them do not look like a configuration change. Reviewing a log is not a change — but which records were read by whom is one of the questions asked in an incident investigation. Installing a certificate looks like copying a file, yet it changes who the machine trusts.
In the script-and-repo form, this number drops to 0/10, because the repository commit depends on the job, not the form: even if the subsystem does not keep a record, the change is written down somewhere. The fix for a job that leaves no trace is not changing the interface — it is turning the job into a defined change.
Forty Machines
The third table adds scale. When the ten jobs are repeated across forty branch servers, the number of manual operations is 400 in the graphical and command forms and 10 in the script-and-repo form; the definition is written once and run forty times.
The number of places storing the change definition is 0, 0, and 1. Zero means there is no definition to compare against to see whether the forty machines have drifted from one another; drift can only be found by reading the machines one by one. If a definition exists, drift is asked as the difference between the definition and the machine.
The items appearing in the audit trail rise to 440, 440, and 840. This number does not improve as it grows; it is the number of items that must be read. The measure’s third column is therefore not the raw record count but the number of questions answered per record: 440 items answer “who, when”; 400 of the 840 items also answer “exactly what.”
The Record’s Readability Is a Separate Item
The number of items appearing in the audit trail assumes those items can be read together. The 440 records across forty machines turn into a single narrative only under two conditions: the records sharing a common time axis, and the same job being referred to by the same name across different machines.
The first condition depends on the time-synchronization item from the previous lesson. If that
item is off, the records of two machines cannot be ordered, and the question “which happened
first” goes unanswered; the number of items appearing in the audit trail keeps being written as
440, but the number of questions it answers drops. The second condition concerns naming: if the
same job is recorded under one name on one machine and a different name on another, the items
cannot be aggregated.
This is why the trace measure is written with three numbers, not two: how many items drop, how many are persistent, and how many of the persistent ones are comparable. The first two numbers depend on the form and the subsystem; the third depends on install-time items. When the third number is not measured, the audit trail is just a pile of records.
The Choice of Form Depends on How Often the Job Repeats
The table does not rank one form above the other. The numbers separate three situations.
A one-off job with no definition yet. While hunting for the cause of a problem, which setting will change is not known in advance; here, the graphical interface’s missing trace is not a cost, because there is no definition to repeat. This kind of job is not repeated across forty machines.
A job that will be repeated across forty machines. In this situation, the third table points in a single direction: the gap between 400 manual operations and 10 is less about the operation count and more about how measurable the drift is. On forty machines with no definition, drift cannot be found.
A job that is subject to audit. What decides here is not the form but whether a subsystem record exists behind the job. If the subsystem does not keep a record, no form fixes this; the job must be turned into a defined change.
The common lesson across the three situations is this: the choice of form is not a habit — it is a decision determined by how often the job repeats and by the audit requirement. Once it is written down how many of the measurement network’s ten jobs fall into which class, which job needs to be scripted stops being a matter of debate.
Summary
- A management job’s trace is written with three numbers: record items dropped, how many of them appear in the audit trail, and how many jobs can be reproduced.
- The graphical interface and the command line give the same numbers on the ten jobs: 21 records, 11 audit trail items, 3 jobs leave no trace at all. The audit trail comes not from the form but from the subsystem keeping its own record and from the change ticket.
- The item each form adds on its own is not persistent; shell history is a file the person who did the job can delete, and an audit trail item sits in a place not under the control of the person who left it.
- The reproducible job count is 0/10, 0/10, and 10/10. The distinction is not between graphical and command but between scripted and unscripted work; the definition must sit somewhere that cannot be altered.
- Manual operations across forty machines drop from 400 to 10, and the place storing the change definition rises from 0 to 1; drift can only be asked about if a definition exists to compare against.
- The number of items appearing in the audit trail assumes the items sit on a common time axis and under common names; without these conditions, the item count does not change but the number of questions it answers drops.
Next Step
Everything measured across this topic has been about how a scheme is built: which boundary stands where, which item closes when, which policy can be expressed, which job leaves a trace. All of it holds while the machine works as expected.
When the machine does not work as expected, only symptoms are left, and the records counted in this lesson are where those symptoms are read. The next lesson looks at that situation and ties the method to a number: for a set of symptoms, how many possibilities does each hypothesis eliminate, how many steps does each sequence take to finish, and how many steps does the worst sequence take.
To keep your progress and take notes, Log in
My notes
Log in to take notes.