Partitions and PKeys: the most common mistake in IB
S2·E3The tenant partition went in and half the cluster stopped talking · Hotel lobby, 02:20, a change window that was supposed to end at midnight
Builds on: Bringing up OpenSM
Before you read: what do you already know?
3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.
After this lesson you can
- Write a partitions.conf that produces the membership you intended using the documented grammar and defaults.
- Analyse the default-partition asymmetry and predict which ports become limited members when a partition file appears.
- Derive who can talk to whom from full and limited membership and explain the pair that cannot.
- Verify a partition design against the fabric with smpquery and ibdiagnet instead of inferring it from the file.
Episode 3 — The tenant partition went in and half the cluster stopped talking
Six hours ago this fabric was fine. Then the co-op’s tenant isolation went in - one partitions.conf, one SM reload - and the sentence on the bridge is one you have heard before: half the nodes joined, ibstat says Active, MPI hangs. The change owner wants to roll back storage firmware that was never in the change. Acceptance is Monday.
Partitions exist because one physical subnet carries traffic that should not mix: tenants, storage, management. A P_Key is the token that decides who may talk to whom, and it carries two levels in one value - the high bit is the membership type, ‘1’ full and ‘0’ limited, which is why 0xFFFF and 0x7FFF are the same partition seen at two levels.[2] Tonight’s outage is one sentence deep: a match occurs only if at least one of the two nodes is a full member, so two limited members of the same partition cannot exchange traffic at all.[2]
Someone reads the new file onto the call: a tenant line per hospital group, and no Default rule. The moment that file appeared, every end port not named in it became a limited member of 0x7fff - the only partition the compute nodes still share.[1] On the floor, the night-shift operator is reading labels aloud to find the SM host; the one that says DO NOT POWER OFF is on a staging box that has not been master since Tuesday.
A partition file is a list of exceptions: whatever it does not name, it demotes.
Segment 1 reads the grammar that wrote tonight.
1What a P_Key is, and the bit that decides everything
NVIDIA frames partitions as the IB analogue of VLANs: “InfiniBand (IB) provides network isolation through network partitions, like Ethernet VLANs (802.1Q).”[2] The mechanism is a 16-bit Partition Key carried in the packet’s BTH — “The P_Key is embedded in the packet’s BTH (L4 header) and is enforced by both the HCA and the switch” — and it applies to “all IB packets, except for SMP MADs.”[2] That exception is why a fabric with a broken partition design still answers smpquery and ibdiagnet while applications hang.
The layout is where partitions stop resembling VLANs. “15 LSB (P_Key[14:0]): This field defines the P_KEY value… 1 MSB (P_Key[15]): This field defines the membership type. ‘1’ means full member, ‘0’ means limited member.”[2] So 0xFFFF and 0x7FFF are the same partition at two membership levels — “The default P_Key value is 0xFFFF/0x7FFF, depending on the membership type.”[2] OpenSM’s config file takes only the value half: “PKey — P_Key value for this partition. Only low 15 bits will be used. When omitted will be autogenerated.”[1]
And here is the rule that produces most of the tickets: “A P_Key match occurs only if at least one of the nodes (sending / receiving) is a full member. Full members can send packets to both full and limited members; limited members can only send packets to full members.”[2] Two limited members of the same partition therefore cannot exchange traffic at all. Worth knowing where that fact lives: the opensm man page never states it, and only NVIDIA’s own security page spells it out — so cite that page, not the man page, when a customer asks you to prove it.[2][1]
2The asymmetry that breaks working clusters
OpenSM always has a default partition: “The default partition will be created by OpenSM unconditionally even when partition configuration file does not exist or cannot be accessed. The default partition has P_Key value 0x7fff. OpenSM’s port will always have full membership in default partition.”[1]
The trap is what happens to everyone else, and the man page states it in one sentence. All other end ports “will have full membership if the partition configuration file is not found or cannot be accessed, or limited membership if the file exists and can be accessed but there is no rule for the Default partition.”[1] Written as the two implicit rules it means:[1]
# no /etc/opensm/partitions.conf at all -> behaves as:
Default=0x7fff : ALL=full ;
# a partitions.conf exists with no Default rule -> behaves as:
Default=0x7fff : ALL=limited, SELF=full ;Combine that with the match rule from segment 1 and you have the failure in full. Every compute node is now a limited member of 0x7fff; limited members can only talk to full members; the only full member is the SM’s own port. The fabric is Active everywhere, ibstat is clean, ibdiagnet runs — and MPI hangs. Nothing looks broken because nothing is broken at the link layer.
The grammar you need to avoid it is short. A definition is [PartitionName][=PKey][,indx0][,ipoib_bc_flags][,defmember=full|limited] followed by a colon, a port-GUID list, and a semicolon.[1] defmember=full|limited|both “specifies default membership for port guid list. Default is limited”, and a port written as <PortGUID>[=[full|limited|both]] with the suffix “omitted (or unrecognized)” is assumed limited — so a typo demotes a node just as effectively as an omission.[1] The keywords are ALL, ALL_CAS, ALL_SWITCHES, ALL_ROUTERS and SELF, the last meaning the subnet manager’s port, and “Empty list means no ports in this partition.”[1]
Two more rules that bite on copy-paste. “PartitionName does not need to be unique, PKey does need to be unique. If PKey is repeated then those partition configurations will be merged and first PartitionName will be used.”[1] And the man page gives the safe first line outright: “The following rule is equivalent to how OpenSM used to run prior to the partition manager: Default=0x7fff,ipoib:ALL=full;”.[1]
* = inherited from defmember.- errorThe file exists but has no rule for the Default partition: every end port is silently demoted to LIMITED membership on 0x7fff. Nothing changed on the hosts — the file appearing is the event.End ports "will have full membership if the partition configuration file is not found or cannot be accessed, or limited membership if the file exists and can be accessed but there is no rule for the Default partition."
- errorgpu01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
- errorgpu02 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
- errorstor01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
- errorviz01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
- errorgpu01 is a LIMITED member of ipoib partition tenant-a (0x7ff1): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
- errorgpu02 is a LIMITED member of ipoib partition tenant-a (0x7ff1): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
"defmember=full|limited|both — specifies default membership for port guid list. Default is limited." A GUID written without "=full" inherits it; an unrecognised suffix is also read as limited.
"indx0 — indicates that this pkey should be inserted in block 0 index 0." That slot is what the IPoIB driver reads: it "creates one interface for each port using the P_Key at index 0".
FAE angle. Three lines of answer: own PKey 0x7ff1 with defmember=full inside the tenant, limited membership on 0x7fff outside it, part_enforce=both so the leaf switch drops the mismatch instead of trusting the HCA.
Switch-embedded SM (MLNX-OS): ib partition my-partition pkey 0x7ff2 → partition my-partition → member all; read back with show ib partition. It applies "only when the SM is enabled and running on the system."
# /etc/opensm/partitions.conf — opensm -P /etc/opensm/partitions.conf -Z both tenant-a=0x7ff1,indx0,ipoib : 0x0002c90300a1b201, 0x0002c90300a1b301 ;
Definition: [PartitionName][=PKey][,indx0][,ipoib_bc_flags][,defmember=full|limited] then " : " a port-GUID list and " ; ". Keywords: ALL, ALL_CAS, ALL_SWITCHES, ALL_ROUTERS, SELF (the subnet manager's port). "Empty list means no ports in this partition."
"The following rule is equivalent to how OpenSM used to run prior to the partition manager: Default=0x7fff,ipoib:ALL=full;"
3Designing two tenants, and what NVIDIA recommends
The multi-tenant answer is three decisions, and they follow directly from the match rule.
First, keep 0x7fff as the management partition but demote the tenants in it. NVIDIA’s own hardening guidance is to “Define all end nodes as limited members on the default PKEY” with default_membership = limited.[4] Management stays reachable because the management node is a full member; tenants cannot reach each other over the default partition because two limited members never match.[2]
Second, give each tenant its own PKey with full membership inside it, so that tenant nodes can talk to their own peers:[1]
Default=0x7fff, ipoib : ALL=limited, SELF=full ;
tenant-a=0x7ff1, ipoib, indx0, defmember=full : 0x0002c90300a1b201, 0x0002c90300a1b301 ;
tenant-b=0x7ff2, ipoib, defmember=full : 0x0002c90300c7d101 ;
storage=0x7ff8, defmember=limited : 0x0002c90300a1b201, 0x0002c90300c7d101, 0x0002c90300b4c101=full ;Read the last line carefully: the storage target is the only full member, every client is limited, so clients reach storage and not each other. That is the hub-and-spoke pattern the membership bit was designed for.
Third, make the switch do the enforcement rather than trusting the hosts. -Z/--part_enforce [both | in | out | off] sets “the partition enforcement type (for switches)… Default is both”, and UFM ships part_enforce = both (default- outbound and inbound) marked read-only.[1][11] Leave it at both. Related and off by default: -W/--allow_both_pkeys decides “whether both full and limited membership on the same partition can be configured in the PKeyTable. Default is not to allow both pkeys.”[1]
Two side effects to plan for. If a partition will carry IPoIB it needs the ipoib flag, which “indicates that this partition may be used for IPoIB, as a result the IPoIB broadcast group will be created with the mgroup_flag flags given, if any” — and the group’s defaults are rate=3, mtu=4 (2048), sl=0, scope=2, Q_Key=0x0b1b for IP groups, with an explicit “WARNING: changing this [Q_Key] for the broadcast group may break IPoIB on client nodes!!”[1] And RFC 4391 requires that “a ‘Full Membership’ P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another”, with every IPoIB interface FullMember-joining the broadcast group.[8] A tenant partition of limited members with the ipoib flag therefore produces an interface that comes up and never resolves ARP.[8][9]
On the switch side the same objects exist in MLNX-OS — ib partition my-partition pkey 0x7ff2, then partition my-partition and member all, inspected with show ib partition — with the caveat that “The partitions configuration is applicable and to be used only when the SM is enabled and running on the system.”[5] The UFM appliance CLI exposes the management partition’s default membership as ib partition management defmember <full|limited>, and “It is not possible to modify the defmember in case OpenSM or UFM are running.”[10]
4Verify against the fabric, never against the file
A partition design is only real once the SM has programmed it, and there are three read-back paths.
Per port, from the SMA and with no SA involvement, smpquery takes an operation name and a LID and port: smpquery PKeys <lid> <port> returns that port’s PKey table, in the same family as smpquery SL2VL and smpquery VLArb.[6] This is the narrowest possible check and the one to run on the one node that cannot reach its peer.
Fabric-wide, ibdiagnet writes ibdiagnet2.pkey — “pkey tables” — alongside ibdiagnet2.slvl and ibdiagnet2.sm.[7] Ask a customer for that file rather than for their partitions.conf, because the file states intent and the dump states outcome, and the whole class of bug in this lesson lives in the gap between the two.
On the host, IPoIB exposes what it is actually using: the driver “creates one interface for each port using the P_Key at index 0”, child interfaces are created with echo 0x8001 > /sys/class/net/ib0/create_child producing ib0.8001, and /sys/class/net/ib0/pkey reports the key in force.[12] NVIDIA’s DOCA page documents the same mechanism in shorthand — echo 1 also yields ib0.8001, because “the actual PKey used is a 16-bit number with the most significant bit set”.[9] That is where indx0 connects to something visible: the partition marked indx0 is the one the base interface picks up.[1][12]
Case: a customer added a storage tenant last night. This morning 24 of 48 nodes cannot run MPI jobs with each other. All ports are Active, ibdiagnet runs clean, IPoIB interfaces exist on every host.
- Read intent first, on the SM host:
Expected in this failure: acat /etc/opensm/partitions.confstorage=0x7ff8 : …stanza and noDefaultrule. That single omission demotes every end port to limited membership of 0x7fff.[1] - Confirm the SM is loading the file you just read. Check the command line for
-P/--Pconfig, or the config file for the partition path; MLNX_OFED documents the default as/etc/opensm/partitions.conf, and a UFM-managed fabric keeps its own copy under/opt/ufm/files/conf/opensm/.[1][11] - Read reality fabric-wide:
Expected:ibdiagnet sed -n '1,60p' /var/tmp/ibdiagnet2/ibdiagnet2.pkey grep -n -i '0x7fff' /var/tmp/ibdiagnet2/ibdiagnet2.pkey | head -20ibdiagnet2.pkeyshows compute ports holding 0x7fff as limited members. Note the output directory the run printed — the documentation disagrees between/var/tmp/ibdiagnet2/and/var/tmp/ibdiagpath/, so take the path from the run.[7] - Narrow to one port to make the evidence undeniable:
Expected: the table contains 0x7fff without the membership bit set, matching the dump.[6]smpquery PKeys <lid> 1 - Explain the symptom from the rule, not from a guess: every compute port is now a limited member of the only partition they share, and a match requires at least one full member — so compute-to-compute traffic on 0x7fff has no valid key pair.[2]
- Fix minimally. Add the Default rule the file was missing, deciding deliberately between the permissive form and the hardened one:
The second is NVIDIA’s guidance for multi-tenancy and requires that each tenant also has its own partition with# restore prior behaviour exactly Default=0x7fff, ipoib : ALL=full ; # or the hardened multi-tenant posture Default=0x7fff, ipoib : ALL=limited, SELF=full ;defmember=full, or you have re-created the outage on purpose.[4][1] - Apply and verify in the same order you diagnosed. Reload the SM, then re-run
ibdiagnetand re-readibdiagnet2.pkey, thensmpquery PKeyson the same port as step 4, then run the MPI job.[7][6] Rollback: restore the previouspartitions.conffrom backup and reload. - Close the case with the two artifacts that prove it: the before-and-after
ibdiagnet2.pkeyand the diff ofpartitions.conf.[7]
cat /etc/opensm/____.conf. Look for a____rule; its absence demotes every unlisted end port to ____ .- Confirm the SM loads that path via
-____or the config file - UFM keeps its own under/opt/ufm/files/conf/opensm/. ibdiagnet, then readibdiagnet2.____for the programmed tables. Take the output directory from the ____ , not from the docs.smpquery ____ <lid> 1on one suspect port.- Explain: a match needs at least one ____ member, so ____ to ____ on 0x7fff cannot match.
- Fix: add
Default=0x7fff, ipoib : ALL=____ ;to restore, orALL=____ , SELF=full ;for the hardened posture plus a per-tenant partition withdefmember=____. - Reload, then verify in the order ____ then ____ then the job. Rollback: restore the previous file and reload.
- Attach the before and after ____ file plus the config diff.
A CSP customer runs three tenants plus a shared all-flash storage appliance on one fabric. Their requirements: tenants must not reach each other at all; every tenant must reach storage; the operator’s UFM host must reach everything; and each tenant needs a working IPoIB network of its own. They currently have one partition file containing only Default=0x7fff,ipoib:ALL=full;.
Produce the design. Acceptance criteria: (a) the complete partitions.conf with every membership suffix explicit and a comment naming why each one is full or limited; (b) the per-port membership matrix and the who-can-talk-to-whom matrix it implies, including the pairs that are blocked by the membership rule rather than by separate partitions; (c) the two flags that decide whether the storage partition and each tenant partition can carry IPoIB and which partition the base ib0 interface will use; (d) the enforcement setting you require and why host-side checking is not sufficient; (e) the exact verification commands and files you will run after the change, and the one that would prove a tenant can still reach another tenant if you got it wrong.
What the dump said and the file did not
ibdiagnet2.pkey and a single smpquery PKeys on one stranded port agree: 0x7fff present, membership bit clear.[7][6] The Default rule goes back in ahead of the tenant lines, the SM reloads, MPI runs, and the case notes close on two artifacts - the before-and-after dumps and the diff of partitions.conf.[1][7]
What you say at 03:10: “Your file said what you intended. The fabric did what the file left out.”
At 03:40 the storage group posts in the same channel. Since the tenants went in, their reads collapse every time a training job ramps - and they have a printed QoS policy that says this cannot happen.
Lab
Goal: apply a two-tenant partition file on the Dell lab and read it back off the ports. Step 4 changes fabric-wide membership; its rollback is named and must be tested before you start.
- Pre-flight inventory, read-only:
Expected: port GUIDs and LIDs for both hosts,sudo cp /etc/opensm/partitions.conf /root/partitions.conf.bak 2>/dev/null || echo "no existing file - that itself is the ALL=full case" ibstat -p ibstat | grep -E "Base lid|State:" smpquery PKeys <lid> 1 | tee /tmp/pkeys-before.txt cat /sys/class/net/ib0/pkey 2>/dev/nullActiveports, a PKey table containing 0x7fff with full membership, and0x7ffffrom sysfs if IPoIB is up.[6][12] If there was no existing partitions.conf, note it: your rollback is deleting the file, not restoring one. - Write the file using the real GUIDs from step 1, keeping the Default rule first:
Note that this Default line is the permissive one on purpose: change one thing per lab.[1]Default=0x7fff, ipoib : ALL=full ; tenant-a=0x7ff1, ipoib, indx0, defmember=full : <host_a_guid>, <host_b_guid> ; - Dry-check before applying: re-read your file against the grammar and confirm both GUIDs are port GUIDs from
ibstat -p, not node GUIDs. A node GUID here produces a partition with no members and no error.[1] - Mutating — point the SM at the file and reload. Rollback: restore
/root/partitions.conf.bak(or delete the file if there was none) and reload the SM the same way.
Expected: one furthersudo kill $(cat /var/run/opensm.pid) sudo opensm -g <port_guid> -p 13 -P /etc/opensm/partitions.conf -B -J /var/run/opensm.pid sleep 20 grep -c "SUBNET UP" /var/log/opensm.logSUBNET UP.[1] - Read back what was programmed, read-only:
Expected: 0x7ff1 now present with full membership alongside 0x7fff.[6]smpquery PKeys <lid> 1 | tee /tmp/pkeys-after.txt diff /tmp/pkeys-before.txt /tmp/pkeys-after.txt - Bring up the tenant’s IPoIB child interface and use it, read-only in effect:
Expected:echo 0xfff1 | sudo tee /sys/class/net/ib0/create_child ip addr add 10.10.1.1/24 dev ib0.fff1 && ip link set ib0.fff1 up ping -c3 10.10.1.2ib0.fff1exists — 0xfff1 is the full-membership form of the tenant key 0x7ff1, because “the actual PKey used is a 16-bit number with the most significant bit set”, which is also what RFC 4391 requires of an IPoIB partition — and it pings its peer once both sides are configured.[9][8] Rollback:ip link set ib0.fff1 downthenecho 0xfff1 | sudo tee /sys/class/net/ib0/delete_child— “when deleting the interface you must use the PKey value with the most significant bit set”.[9] - Reproduce the outage deliberately, mutating. Comment out the
Defaultline, reload the SM, and re-readsmpquery PKeysand the ping. Expected: 0x7fff now appears as a limited membership and cross-node traffic on the default partition stops while the tenant partition keeps working.[1][2] Rollback: uncomment the line and reload. - Fabric-wide evidence, read-only:
ibdiagnet, then readibdiagnet2.pkeyfrom the directory the run printed and confirm it agrees withsmpquery.[7] Keep the before-and-after copies — that pair is the artifact you would attach to a customer case. - Restore: put back the original file (or remove it), reload the SM, re-run step 5’s diff until it is empty, and delete the child interface if you have not already.
Goal: write three partition files and predict their effects exactly, with no fabric. All steps are file and documentation work.
- Write file A, the legacy-equivalent one line:
Default=0x7fff,ipoib:ALL=full;.[1] Predict and write down: every end port’s membership in 0x7fff, and whether any pair is blocked. - Write file B, a two-tenant isolation design with a shared storage target, using the pattern from segment 3. Every port specifier must carry an explicit
=fullor=limitedsuffix even where thedefmemberalready covers it — the exercise is to stop relying on defaults you cannot see in the file. - Write file C, deliberately broken: file B with the
Defaultline deleted. Predict the resulting membership of every port and which pairs stop communicating, then write the one-sentence explanation you would give a customer.[1][2] - Validate the syntax against the documentation rather than a parser: for each file, check the definition against
[PartitionName][=PKey][,indx0][,ipoib_bc_flags][,defmember=full|limited], confirm every PKey is unique, and confirm you used only the documented keywordsALL,ALL_CAS,ALL_SWITCHES,ALL_ROUTERSandSELF.[1] Expected: at least one of your three files has a repeated or autogenerated PKey on the first attempt. - Build all three in the designer above and compare its can-talk-to matrix against your predictions. Expected: the file C matrix greys out every compute-to-compute pair. Where the tool disagrees with your prediction, write down which documented rule you missed.
- Mark your evidence honestly. In your file C explanation, underline the claim that rests on the full-versus-limited match rule and note that it comes from NVIDIA’s InfiniBand security page — the
opensmman page states the membership defaults but never spells out that two limited members cannot communicate.[2][1] - Course caveat: whether NVIDIA Air’s free tier can simulate an InfiniBand fabric well enough to run a real partition file was not confirmed when this lesson was written — the Air supported-platforms page did not resolve. Do not plan a customer demo on it without checking first.
Retrieval check
10 questions from memory. Answer before looking anything up; misses become flashcards.
Explain it to a Dell SE
A Dell customer wants two tenants isolated on one InfiniBand fabric and asks whether it works like VLANs. Explain in five sentences how partitions differ and name the one mistake that will break their cluster.
Sources
Facts in this lesson were checked against opensm(8) man page (linux-rdma master) re-fetched 2026-09-07 (default-partition paragraph, grammar, defmember default limited, part_enforce both, allow_both_pkeys off); NVIDIA InfiniBand Security overview re-fetched 2026-09-07 (P_Key bit 15 membership, full-versus-limited match rule). Dates are when each page was fetched.
- opensm(8) man page source (linux-rdma/opensm master) · fetched 2026-09-07
- Security in InfiniBand - NVIDIA InfiniBand Security Overview and Guidelines · fetched 2026-09-07
- OpenSM partitions.conf configuration reference · fetched 2026-09-07
- NVIDIA InfiniBand Security: Practical Guidelines · fetched 2026-09-07
- Subnet Manager - NVIDIA MLNX-OS User Manual v3.12.6200 LTS · fetched 2026-09-07
- smpquery(8) man page (rdma-core master) · fetched 2026-09-07
- ibdiagnet Dump Files - IBUtils2 Utility Documentation · fetched 2026-09-07
- RFC 4391 - Transmission of IP over InfiniBand (IPoIB) · fetched 2026-09-07
- IP Over InfiniBand - DOCA 3.5.0 · fetched 2026-09-07
- InfiniBand Commands - UFM Enterprise Appliance Software 1.6.0 · fetched 2026-09-07
- UFM Enterprise 6.24.1: UFM Subnet Manager Default Properties · fetched 2026-09-07
- Linux kernel IPoIB documentation - Partitions and P_Keys · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.