Property Layers: Multi-Level Configuration
> Implementation guide for Gas Town's configuration system. > Created: 2025-01-06
Overview
Gas Town uses a layered property system for configuration. Properties are looked up through multiple layers, with earlier layers overriding later ones. This enables both local control and global coordination.
The Four Layers
┌─────────────────────────────────────────────────────────────┐
│ 1. WISP LAYER (transient, town-local) │
│ Location: <rig>/.beads-wisp/config/ │
│ Synced: Never │
│ Use: Temporary local overrides │
└─────────────────────────────┬───────────────────────────────┘
│ if missing
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. RIG BEAD LAYER (persistent, synced globally) │
│ Location: <rig>/.beads/ (rig identity bead labels) │
│ Synced: Via git (all clones see it) │
│ Use: Project-wide operational state │
└─────────────────────────────┬───────────────────────────────┘
│ if missing
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. TOWN DEFAULTS │
│ Location: ~/gt/config.json or ~/gt/.beads/ │
│ Synced: N/A (per-town) │
│ Use: Town-wide policies │
└─────────────────────────────┬───────────────────────────────┘
│ if missing
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. SYSTEM DEFAULTS (compiled in) │
│ Use: Fallback when nothing else specified │
└─────────────────────────────────────────────────────────────┘
Lookup Behavior
Override Semantics (Default)
For most properties, the first non-nil value wins:
func GetConfig(key string) interface{} {
if val := wisp.Get(key); val != nil {
if val == Blocked { return nil }
return val
}
if val := rigBead.GetLabel(key); val != nil {
return val
}
if val := townDefaults.Get(key); val != nil {
return val
}
return systemDefaults[key]
}
Stacking Semantics (Integers)
For integer properties, values from wisp and bead layers add to the base:
func GetIntConfig(key string) int {
base := getBaseDefault(key) // Town or system default
beadAdj := rigBead.GetInt(key) // 0 if missing
wispAdj := wisp.GetInt(key) // 0 if missing
return base + beadAdj + wispAdj
}
This enables temporary adjustments without changing the base value.
Blocking Inheritance
You can explicitly block a property from being inherited:
gt rig config set gastown auto_restart --block
This creates a "blocked" marker in the wisp layer. Even if the rig bead
or defaults say auto_restart: true, the lookup returns nil.
Rig Identity Beads
Each rig has an identity bead for operational state:
id: gt-rig-gastown
type: rig
name: gastown
repo: git@github.com:steveyegge/gastown.git
prefix: gt
labels:
- status:operational
- priority:normal
These beads sync via git, so all clones of the rig see the same state.
Two-Level Rig Control
Level 1: Park (Local, Ephemeral)
gt rig park gastown # Stop services, daemon won't restart
gt rig unpark gastown # Allow services to run
- Stored in wisp layer (
.beads-wisp/config/) - Only affects this town
- Disappears on cleanup
- Use: Local maintenance, debugging
Level 2: Dock (Global, Persistent)
gt rig dock gastown # Set status:docked label on rig bead
gt rig undock gastown # Remove label
- Stored on rig identity bead
- Syncs to all clones via git
- Permanent until explicitly changed
- Use: Project-wide maintenance, coordinated downtime
Daemon Behavior
The daemon checks both levels before auto-restarting:
func shouldAutoRestart(rig *Rig) bool {
status := rig.GetConfig("status")
if status == "parked" || status == "docked" {
return false
}
return true
}
Configuration Keys
| Key | Type | Behavior | Description |
status | string | Override | operational/parked/docked |
auto_restart | bool | Override | Daemon auto-restart behavior |
max_polecats | int | Override | Maximum concurrent polecats |
priority_adjustment | int | Stack | Scheduling priority modifier |
maintenance_window | string | Override | When maintenance allowed |
dnd | bool | Override | Do not disturb mode |
Commands
View Configuration
gt rig config show gastown # Show effective config (all layers)
gt rig config show gastown --layer # Show which layer each value comes from
Set Configuration
# Set in wisp layer (local, ephemeral)
gt rig config set gastown key value
# Set in bead layer (global, permanent)
gt rig config set gastown key value --global
# Block inheritance
gt rig config set gastown key --block
# Clear from wisp layer
gt rig config unset gastown key
Rig Lifecycle
gt rig park gastown # Local: stop + prevent restart
gt rig unpark gastown # Local: allow restart
gt rig dock gastown # Global: mark as offline
gt rig undock gastown # Global: mark as operational
gt rig status gastown # Show current state
Examples
Temporary Priority Boost
# Base priority: 0 (from defaults)
# Give this rig temporary priority boost for urgent work
gt rig config set gastown priority_adjustment 10
# Effective priority: 0 + 10 = 10
# When done, clear it:
gt rig config unset gastown priority_adjustment
Local Maintenance
# I'm upgrading the local clone, don't restart services
gt rig park gastown
# ... do maintenance ...
gt rig unpark gastown
Project-Wide Maintenance
# Major refactor in progress, all clones should pause
gt rig dock gastown
# Syncs via git - other towns see the rig as docked
bd sync
# When done:
gt rig undock gastown
bd sync
Block Auto-Restart Locally
# Rig bead says auto_restart: true
# But I'm debugging and don't want that here
gt rig config set gastown auto_restart --block
# Now auto_restart returns nil for this town only
Implementation Notes
Wisp Storage
Wisp config stored in .beads-wisp/config/<rig>.json:
{
"rig": "gastown",
"values": {
"status": "parked",
"priority_adjustment": 10
},
"blocked": ["auto_restart"]
}
Rig Bead Labels
Rig operational state stored as labels on the rig identity bead:
bd label add gt-rig-gastown status:docked
bd label remove gt-rig-gastown status:docked
Daemon Integration
The daemon's lifecycle manager checks config before starting services:
func (d *Daemon) maybeStartRigServices(rig string) {
r := d.getRig(rig)
status := r.GetConfig("status")
if status == "parked" || status == "docked" {
log.Info("Rig %s is offline, skipping auto-start", rig)
return
}
d.ensureWitness(rig)
d.ensureRefinery(rig)
}
Related Documents
~/gt/docs/hop/PROPERTY-LAYERS.md- Strategic architecturewisp-architecture.md- Wisp system designagent-as-bead.md- Agent identity beads (similar pattern)