Skip to content

πŸŽ™οΈ task - feat(ssm): DeclaredAwsSsmSshTunnel.ttl β€” a tunnel must not outlive its credentialΒ #95

Description

@ehm-a-seaturtle

πŸ¦«πŸŽ™οΈ dispatch to foreman

πŸ’§ task enqueued
   β”œβ”€ priority = ?
   β”œβ”€ yieldage = ?
   └─ leverage = ?

title
feat(ssm): DeclaredAwsSsmSshTunnel.ttl β€” a tunnel must not outlive its credential
description

.what

DeclaredAwsSsmSshTunnel has no declarable lifetime. a tunnel opened by git.grove.wake stays
open until its process dies, and there is no way to say "this tunnel must not outlive N hours."

the ask: a ttl field on the resource, so a tunnel past its ttl reads CLOSED and the next
apply respawns it fresh.

.why the resource is the right home

spawnedAt already exists on the object, and getOneSsmSshTunnel already reads it:

// DeclaredAwsSsmSshTunnel.d.ts
spawnedAt?: string | null;   // @readonly β€” used with pid for reuse detection
static readonly: readonly ['pid', 'spawnedAt'];

so the resource ALREADY reasons about its own age. it records when it started and compares that
against a pid, to catch OS pid reuse. a ttl adds no new state and no new read β€” it adds one more
comparison against a value already in hand.

.the change, at the line it belongs on

getOneSsmSshTunnel.js already gates OPEN behind two live conditions:

const processAlive = isProcessAlive({ pid: cacheFile.pid });
const portReady = processAlive
  ? await isSshTunnelHealthy({ port: input.by.unique.from.port })
  : false;

if (!processAlive || !portReady) return notconnected;   // ← the seam

a ttl is a third condition on that same line:

const withinTtl = !ttl
  || Date.now() - new Date(cacheFile.spawnedAt).getTime() < toMilliseconds(ttl);

if (!processAlive || !portReady || !withinTtl) return notconnected;

ttl as an IsoDuration ('PT12H'), to match the house glossary (rule.require.iso-time).

.why it converges with no reaper

a past-ttl tunnel reads CLOSED, so the very next declastruct apply sees a drift and respawns
it. there is no timer to install, no state file to keep, and no daemon to supervise β€” the age is
DERIVED from spawnedAt on each read.

this is the shape domain.terms/term=idle already validated in the grove's own hibernate clock:
a stored clock plus a reset unit was retired in favor of a derived read off an mtime the disk
already held. same move, same reason β€” a stored deadline can drift from the world, a derived one
cannot.

.the second half β€” the process should carry its own deadline

the get-side ttl makes a stale tunnel read CLOSED. it does not make the process die. for a
true timeout, setSsmSshTunnel must hand the spawned session-manager-plugin a deadline, so the
process terminates on its own at ttl whether or not anyone applies again.

both halves earn their keep, and they answer different questions:

half answers without it
get-side ttl "is this tunnel still valid?" a stale tunnel reads OPEN and is reused
set-side deadline "when does this tunnel die?" the process leaks a local port + an ssm session forever

.why 12h specifically, for the grove case

12h is not an arbitrary number β€” it is the life of the credential that opened the tunnel.

domain.terms/term=tier records that a permission set's sessionDuration caps at PT12H, which
is AWS's hard maximum, and that both everyday-reader and everyday-power sit at that cap. so
the SSO credential behind the tunnel's ssm session expires at 12h by construction.

a tunnel cannot honestly outlive the credential that opened it. today it tries: the process
stays alive, the local port stays bound, and the session behind it goes bad β€” so the failure
presents as alive but unusable, which is precisely the case isSshTunnelHealthy was written to
catch after the fact. a ttl converts that ragged, silent death into a clean, on-time one.

.the ask

  1. add ttl?: IsoDuration | null to DeclaredAwsSsmSshTunnel (a settable field, not @readonly)
  2. gate the get's OPEN verdict on it, at the seam quoted above
  3. hand the deadline to the spawn in setSsmSshTunnel, so the process self-terminates at ttl
  4. mirror it onto DeclaredAwsSsmVpcTunnel β€” that object records no spawnedAt at all today,
    so it cannot even observe its own age, and it has the same credential-expiry exposure

.the transferable shape

a resource that already records its own start time has already paid for a ttl. the field is
the cheap half; the comparison is one line. when you find a spawnedAt with no deadline beside
it, ask what the value was recorded FOR β€” here it was pid-reuse detection, and the same value
answers staleness for free.

and the sharper one, for any tunnel that fronts a credential:

a session must not outlive the credential that opened it. where the credential's life is a
declared number (term=tier: PT12H), the session's ttl is not a policy choice β€” it is that
number, and any longer is a promise the credential cannot keep.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions