carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "crypto/ed25519"
6 "errors"
7 "fmt"
8 "strings"
9 "time"
10)
11
12// TemporaryAppPrefix is the reserved namespace every temporary app lives in
13// (temporary static sites spec, ruling 7). Nothing permanent may be CREATED
14// under it; an app made permanent with `carlos apps keep` keeps its name.
15// The reservation is what makes "brand new" provable: no permanent app, row
16// or route can hold a tmp- name, so a temporary app's expiry can only drop
17// routes a temporary app created (spec §3).
18const TemporaryAppPrefix = "tmp-"
19
20// IsTemporaryName reports whether app is in the reserved namespace. It says
21// nothing about whether the app IS temporary: a kept app keeps its tmp- name,
22// and only the console's row knows.
23func IsTemporaryName(app string) bool { return strings.HasPrefix(app, TemporaryAppPrefix) }
24
25// TemporaryCooloff is how long a trashed temporary app stays restorable
26// before the console purges it (spec ruling 2). The box's own 30-day
27// TrashCooloff still applies to every app as the backstop.
28const TemporaryCooloff = 72 * time.Hour
29
30// ErrTrashClockMismatch is PurgeAppIfTrashedAt's refusal: the app is in the
31// Trash, but not the trash the purge decision was made about.
32var ErrTrashClockMismatch = errors.New("release: the trash marker is not the one the purge was decided for")
33
34// PurgeAppIfTrashedAt is PurgeApp with one more precondition: the current
35// marker's DeletedAt, truncated to whole seconds, equals wantDeletedAt (the
36// console row's purge_for). A restore followed by a keep and a fresh trash
37// writes a marker with a new clock, so a decision taken about the old trash
38// can no longer fire (spec §5, astra 4).
39//
40// The check runs on the SAME tombstone read PurgeApp's own preconditions use
41// (purgeReady), so there is no second read and no window between two. Every
42// other guard (production flag, object-store estate) applies unchanged.
43func PurgeAppIfTrashedAt(ctx context.Context, s Store, pub ed25519.PublicKey, acct, app string, wantDeletedAt, now time.Time) error {
44 return purgeApp(ctx, s, pub, acct, app, func(t *AppTombstone) error {
45 if t.DeletedAt.Unix() != wantDeletedAt.Unix() {
46 return fmt.Errorf("release: %s/%s was trashed at %s, not %s: %w", acct, app,
47 t.DeletedAt.UTC().Format(time.RFC3339), wantDeletedAt.UTC().Format(time.RFC3339), ErrTrashClockMismatch)
48 }
49 return nil
50 })
51}
52