---
id: 001
status: accepted
title: ADR-001: String Templates for Cloud-Init Generation
---

## Status
Accepted

## Date
2025-12-16

## Context

The ots-containers tool generates cloud-init configuration files for bootstrapping OneTimeSecret deployment environments. This includes generating DEB822-format apt sources lists for Debian 13, PostgreSQL, and Valkey repositories.

During development, proper libraries were discovered:
- **cloud-init-gen**: Programmatic cloud-init YAML generation with validation
- **python-debian (debian.deb822)**: Official DEB822 format handling
- **python-apt**: APT source management

The question arose: Should we refactor from string-based templates to proper libraries?

Key facts:
- Cloud-init functionality: 291 LOC out of 3,309 total (8.8% of codebase)
- Cloud-init is a tertiary feature in a tool primarily about Podman Quadlets and systemd services
- Current implementation has 21 passing tests
- DEB822 format has been stable since 2006
- We control all inputs and outputs (not parsing untrusted data)

## Decision

**Stick with string-based template generation. Do not refactor to specialized libraries.**

This decision is based on proportionality: cloud-init is <10% of functionality, and adding specialized libraries for a minor feature violates the principle of proportional complexity.

**Why this approach works:**

1. **Format stability**: DEB822 has been ossified since 2006. The fields we use (Types, URIs, Suites, Components, Signed-By) are not changing. We're not parsing arbitrary files—we're generating 3 known stanzas.

2. **Controlled scope**: Our YAML structure is trivial (flat dict with package_update, apt.sources_list, packages list). Library abstraction overhead exceeds the complexity it would hide.

3. **Existing verification**: 21 passing tests already validate output correctness. The actual risks (template typos, invalid YAML, invalid DEB822) are already caught by:
   - Test coverage (typos)
   - Validation command (YAML structure)
   - apt-get update on actual systems (DEB822 validity)

4. **No dependency bloat**: We already have PyYAML. Adding cloud-init-gen + python-debian means more supply chain surface, version conflicts, and onboarding complexity.

**When libraries would make sense:**
- Parsing user-provided cloud-init configs (not our use case)
- Building cloud-init tooling as primary product (not our use case)
- Supporting 20+ apt source variations (we have 3 stable stanzas)
- Cloud-init is >25% of codebase scope (we're at 8.8%)

## Trade-offs

**We lose:**
- Industry-standard library validation
- Automatic format evolution if DEB822 spec changes (extremely unlikely)
- "Professional" appearance of using proper libraries

**We gain:**
- Zero additional dependencies
- Simpler codebase (no abstraction layer to understand)
- Faster CI (no library installation overhead)
- Clearer code path (string templates are explicit about what they generate)

**Risk assessment:**
The theoretical risk of "format drift" is mitigated by DEB822's 19-year stability and our test coverage. The practical risk of library version conflicts and supply chain issues is higher than manually maintaining 3 template stanzas.

## Implementation Notes

### Current Template Locations (2025-12-16)
- `src/ots_containers/commands/cloudinit/templates.py` - DEB822 sources generation
- `src/ots_containers/commands/cloudinit/app.py` - Cloud-init YAML assembly
- Tests: `tests/commands/cloudinit/` - 21 passing tests validating output

### When to Revisit This Decision
Reconsider if:
- Cloud-init functionality grows to >25% of codebase
- We need to parse untrusted cloud-init configs from users
- We add support for >10 different repository configurations
- DEB822 format undergoes breaking changes (monitor debian-devel mailing list)
