Agent status token: 12 h (founder decision 65d8aed4, exception to SI-14 for nk-agent-status)
make agent-status-token requests --duration=12h and prints the real expiry from the token's exp claim (tools/agent-status/token_expiry.py, never the token), since the API server may cap a duration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 943911@bnt-lap001 Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
This commit is contained in:
parent
619a359072
commit
06291a97da
6 changed files with 87 additions and 5 deletions
38
tools/agent-status/tests/test_token_expiry.py
Normal file
38
tools/agent-status/tests/test_token_expiry.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
"""token_expiry.py on synthetic JWTs (NK-WP-0058-T05): only the expiry is printed."""
|
||||
import base64
|
||||
import datetime as dt
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[3]
|
||||
spec = importlib.util.spec_from_file_location('token_expiry', REPO / 'tools/agent-status/token_expiry.py')
|
||||
te = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(te)
|
||||
|
||||
|
||||
def jwt(exp):
|
||||
enc = lambda d: base64.urlsafe_b64encode(json.dumps(d).encode()).decode().rstrip('=')
|
||||
return f"{enc({'alg': 'RS256'})}.{enc({'exp': exp, 'sub': 'synthetic'})}.c2lnbmF0dXJl"
|
||||
|
||||
|
||||
def test_prints_expiry_only(tmp_path, capsys):
|
||||
exp = int((dt.datetime.now(dt.timezone.utc) + dt.timedelta(hours=12)).timestamp())
|
||||
token = tmp_path / 't'
|
||||
token.write_text(jwt(exp) + '\n')
|
||||
assert te.main([str(token)]) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert out.startswith('agent-status-token: valid until ')
|
||||
assert '(12.0 h)' in out or '(11.9 h)' in out
|
||||
assert jwt(exp) not in out and 'synthetic' not in out
|
||||
|
||||
|
||||
def test_expired_missing_and_malformed_fail(tmp_path, capsys):
|
||||
old = tmp_path / 'old'
|
||||
old.write_text(jwt(int(dt.datetime(2020, 1, 1, tzinfo=dt.timezone.utc).timestamp())))
|
||||
assert te.main([str(old)]) == 1
|
||||
bad = tmp_path / 'bad'
|
||||
bad.write_text('not-a-token')
|
||||
assert te.main([str(bad)]) == 1
|
||||
assert te.main([str(tmp_path / 'absent')]) == 1
|
||||
assert 'not-a-token' not in capsys.readouterr().out
|
||||
39
tools/agent-status/token_expiry.py
Normal file
39
tools/agent-status/token_expiry.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Print when the agent status token expires, never the token (NK-WP-0058-T05).
|
||||
|
||||
python3 tools/agent-status/token_expiry.py ~/.kube/nk-agent-status.token
|
||||
|
||||
Reads only the JWT's `exp` claim; the signature and every other claim are ignored and nothing else is printed.
|
||||
The API server may cap a requested duration, so this reports the expiry the token really has. Exit 1 when the file
|
||||
is missing, malformed or already expired.
|
||||
"""
|
||||
import base64
|
||||
import datetime as dt
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def expiry(path):
|
||||
parts = Path(path).read_text().strip().split('.')
|
||||
if len(parts) != 3:
|
||||
raise ValueError('not_a_jwt')
|
||||
payload = parts[1] + '=' * (-len(parts[1]) % 4)
|
||||
return dt.datetime.fromtimestamp(int(json.loads(base64.urlsafe_b64decode(payload))['exp']), dt.timezone.utc)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
path = (argv if argv is not None else sys.argv[1:])[0]
|
||||
try:
|
||||
exp = expiry(path)
|
||||
except (OSError, ValueError, KeyError):
|
||||
print('agent-status-token: unreadable')
|
||||
return 1
|
||||
left = exp - dt.datetime.now(dt.timezone.utc)
|
||||
hours = left.total_seconds() / 3600
|
||||
print(f'agent-status-token: valid until {exp:%Y-%m-%d %H:%M}Z ({hours:.1f} h)')
|
||||
return 0 if hours > 0 else 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -17,6 +17,7 @@ PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q -rs -p no:cacheprovider \
|
|||
sso-mfa/k8s/bounded-executor \
|
||||
tools/contract-tests/tests \
|
||||
tools/conventions/tests \
|
||||
tools/agent-status/tests \
|
||||
"$@" | tee "$out"
|
||||
if grep -q '^SKIPPED' "$out"; then
|
||||
echo "contract-test: skipped tests above; every contract must run (are the owner checkouts present?)" >&2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue