Notepat.com M4L Build & Distribution Pipeline #
Versioned build stream for the notepat.com.amxd Max for Live device.
Modeled after the ac-os OTA pattern (fedac/native/ac-os): every
lith deploy that touches an amxd input rebuilds from the deployed
commit, versions the artifact by git hash, and pushes to DO Spaces as
the long-term archive.
Layout #
Local (repo + lith filesystem) #
system/public/m4l/
├── notepat.com.amxd ← current alias (committed)
└── notepat.com/
├── latest.json ← manifest (committed, drives staleness check)
└── <git-hash>.amxd ← immutable versioned build (gitignored)
DO Spaces archive #
s3://assets-aesthetic-computer/m4l/notepat.com/
├── notepat.com.amxd ← root alias (non-cached)
├── latest.json ← rolling pointer (no-cache)
└── <git-hash>.amxd ← immutable (max-age=31536000,immutable)
CDN: https://assets.aesthetic.computer/m4l/notepat.com/<hash>.amxd
User-facing permalink #
https://notepat.com/amxd
└─ Caddy rewrite → /m4l/notepat.com.amxd (lith-served)
with Content-Disposition: attachment
Commands #
npm run notepat:build— build to local paths onlynpm run notepat:build:desktop— also drop a copy on~/Desktopnpm run notepat:publish— build + sync to DO Spaces from dev machine (requiresAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYorDO_SPACES_KEY/DO_SPACES_SECRETin env)fish lith/deploy.fish— deploys lith; rebuilds the amxd on the server (with--if-stale) and pushes to DO Spaces (with--sync-spaces) whenspaces/.env(.gpg)is available in the vault
--if-stale #
Skips the rebuild when no amxd-input file changed since the last
successful build. Tracked inputs (INPUT_PATHS in
ac-m4l/build-notepat.mjs):
system/public/aesthetic.computer/disks/notepat-remote.mjssystem/public/aesthetic.computer/bios.mjssystem/public/aesthetic.computer/lib/oven/bundler.mjs
Compares:
- Committed diff (
prev.piece_git…HEAD) under those paths - Uncommitted working-tree changes under those paths (only when dirty)
If both are empty → skip. Otherwise rebuild and print the offending files.
Staleness UX #
bios.mjs fetches /m4l/notepat.com/latest.json on DAW bridge setup,
compares packGit (from the offline bundle) against piece_git. The
notepat-remote piece surfaces UPDATE AVAILABLE inside its
case-doors readout when the two differ.
Future options #
These are tracked for later — nothing urgent.
1. Move the archive to releases-aesthetic-computer #
The ac-os OTA already lives in
s3://releases-aesthetic-computer/os/, with builds/<slug>.vmlinuz
for versioned kernels and latest-manifest.json for the rolling
pointer. Mirroring that layout for m4l would strictly parallel the
OS pattern:
s3://releases-aesthetic-computer/m4l/notepat.com/
├── builds/<git-hash>.amxd
├── notepat.com.amxd
└── latest.json
Requires:
- Caddy rule for
releases.aesthetic.computer(or update the piece to fetch from thereleases-*CDN subdomain) - Migrate existing archived builds from
assets-*/m4l/notepat.com/(or leave them — they stay reachable under the old path)
2. Garbage-collect old <hash>.amxd builds on S3 #
Every deploy with an amxd-input change adds a new immutable
<hash>.amxd object. After a few dozen commits the bucket will
accumulate obsolete versions. Options:
- A scheduled cron on lith (or as part of deploy.fish) that keeps the latest N builds + anything under 30 days old, deletes the rest.
- Mirror ac-os's
builds/subdirectory with slug names + retention policy.
3. Version-browser page under /ableton #
All published versions sit at predictable paths, so a
ListObjectsV2 against the bucket can power a "browse past
releases" section on the ableton piece. Users could roll back to a
specific hash if a recent build regressed, or compare SHA-256s when
something looks off.
Would need:
- Backend endpoint listing
m4l/notepat.com/<hash>.amxdobjects (either a cached JSON or a live S3 list) - UI in
disks/ableton.mjsto render the list + per-version download buttons linking to the S3/CDN URLs
4. Auto-prune system/public/m4l/notepat.com/ on lith #
Similar to (2) but for the local filesystem on lith. Currently
<hash>.amxd files accumulate in /opt/ac/system/public/m4l/ notepat.com/ (they're gitignored so they don't sync down). Not
user-visible unless someone guesses the URL structure, but eats
disk. A find -mtime +30 -delete in deploy.fish would handle it.
5. Multi-device generalization #
build-notepat.mjs is hardcoded to notepat-remote. If another
piece gets promoted to a flagship amxd, the script should generalize
to a list (like ac-m4l/devices.json already has). Would refactor
into ac-m4l/build-device.mjs <piece-name> and update the deploy
step to iterate devices flagged featured: true.
Credential sources #
| What | Vault path | Used by |
|---|---|---|
AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY |
aesthetic-computer-vault/spaces/.env.gpg |
build-notepat.mjs --sync-spaces, assets:sync:up |
DO_TOKEN |
aesthetic-computer-vault/oven/deploy.env.gpg |
doctl commands, ac-lith droplet lookup |
ART_SPACES_*, AT_BLOBS_SPACES_* |
aesthetic-computer-vault/oven/.env.gpg |
oven/baker.mjs, oven/grabber.mjs |
The deploy flow decrypts spaces/.env.gpg on-the-fly, scp's to
/tmp/notepat-spaces.env on lith for the lifetime of the build step,
and removes it. S3 keys don't persist in /opt/ac/system/.env.
DO Spaces inventory #
As of 2026-04-24 the account has 10 buckets in sfo3:
art-aesthetic-computerassets-aesthetic-computer← m4l archive lives hereat-blobs-aesthetic-computerpals-aesthetic-computerpix.nopaint.artprivate-aesthetic-computerreleases-aesthetic-computer← ac-os OTA targetsotce-aesthetic-computeruser-aesthetic-computerwand-aesthetic-computer
Inspect with:
TMP_ENV=$(mktemp)
gpg --batch --pinentry-mode loopback \
-d aesthetic-computer-vault/spaces/.env.gpg > "$TMP_ENV"
set -a; source "$TMP_ENV"; set +a
aws s3 ls --endpoint-url "$SPACES_ENDPOINT"
aws s3 ls "s3://assets-aesthetic-computer/m4l/notepat.com/" \
--endpoint-url "$SPACES_ENDPOINT"
rm -f "$TMP_ENV"