Repository navigation
Conversation
Tests first, on SQLite and PostgreSQL: the registration-time lifetime cap in both modes (with a released-SDK-shaped root accepted), record versus enforce for roots that outlive their authority, the advertised mode in GET /info, legacy long roots counted, and the current rules for unclassified registrations after the cutover.
Registration refuses a root unless exp <= registration + 31 days (+5 min) and nbf <= exp, in both modes. policy_v3.owner_authority_expiry selects record (default: roots outliving their authority are accepted and counted) or enforce (refused). Unclassified registrations after the cutover get the current rules; earlier ones are counted, never capped. GET /info advertises policy-owner-authority-v1 and the active mode. Adds the owner-authority counters and the counting queries.
…ty-record' into skgbafa/tc-1423-n2-root-lifetime-cap
…ty-record' into skgbafa/tc-1423-n2-root-lifetime-cap # Conflicts: # CHANGELOG.md # tinycloud-node-server/src/policy_v3/owner_authority.rs
|
Review at What I ran (local; SQLite plus a native PostgreSQL 14):
Mutations: removing the cap (both modes, or in either mode alone), flipping the default to
Nits, no change required:
Released SDK roots can't hit the new refusal. Every released signer caps |
…ty-record' into skgbafa/tc-1423-n2-root-lifetime-cap
Unit tests pin the root-lifetime cap at exactly registration + 31 days + 5 minutes and the authority_ttl_bucket edges (1 hour, 30 days). The bucket takes the authority expiry itself (an uncapped root's authority always expires; the unreachable 'none' bucket is gone). The boot log line carries the active owner_authority_expiry with the cutover. Docs: the counters are exported on the Prometheus port (telemetry.enabled; else the counting queries), the cap is described as an expiry bound, and re-posting an existing registration that breaks the current rules gets the same 403.
|
Delta check at Verified:
Remaining:
|
|
APPROVE: see TC-1423 |
…ty-record' into skgbafa/tc-1423-n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
…n2-root-lifetime-cap
|
APPROVE: see TC-1423 (reviewed exact head 0684d2f) |
Linear: TC-1423 (part 2 of 3)
Adds a registration-time cap on policy root lifetime, a record/enforce mode for roots that outlive their owner authority, and the mode advertised in
GET /info. Stacked on #358.Changes
registered_at + 31 days + 5 minutes. The response is403 policy-root-lifetime-exceeded, counted intinycloud_policy_root_lifetime_refused_total.nbfset at signing,exp - nbf <= 31 d) pass.nbfis in the future is refused before the cap runs, by the existing signed time check (policy-root-signature-invalid).policy_v3.owner_authority_expiry(record|enforce, defaultrecord; envTINYCLOUD_POLICY_V3__OWNER_AUTHORITY_EXPIRY):record: a root that outlives the recorded authority is stored asbound-uncappedand counted intinycloud_policy_owner_authority_expiry_exceeded_total{authority_ttl_bucket=le_1h|le_30d|gt_30d}. It is counted once, after commit.enforce: the same root gets403 policy-root-outlives-owner-authority.registered_modeon the binding row records which mode applied.tinycloud_policy_legacy_root_lifetime_exceeded_total.unknownwith reasonlifetime-exceededor, underenforce,outlives-authority.tinycloud_policy_owner_binding_backfill_total{outcome}counts the stored classifications andwrite_failed_served.GET /info//version:featureslistspolicy-owner-authority-v1plus exactly one ofpolicy-owner-authority-recordandpolicy-owner-authority-enforce, before the features that are gated by settings. The mode is read through an infallible guard on the policy runtime. No cache header is added.telemetry.enabledis set. Without it, use the Q1-Q4 counting queries in the docs.docs/policy-owner-authority.mdhas the mode, the cap, the metrics, the Q1-Q4 read-only counting queries and the criteria for switching toenforce. CHANGELOG entry.Stack and merge order
skgbafa/tc-1423-n1-owner-authority-record(record owner authority for policy registrations #358). check owner authority on policy share use #362 stacks on this branch.policy-owner-authority-v1is advertised from this PR, so the release is not cut between cap policy root lifetime and add authority mode #360 and check owner authority on policy share use #362.Tests
Added to
src/policy_v3/owner_authority_tests.rs; each runs on SQLite and on PostgreSQL (CI-only) through the production app.root_lifetime_is_capped_in_both_modesnbfa year out with a 30-day window, andnbf > exp, inrecordandenforce; a released-SDK-shaped root (nbf= signing time,exp = nbf + 31 d) is acceptedenforce_refuses_roots_outliving_their_authorityenforcegives 403;recordstoresbound-uncapped, and thele_1hcounter risesenforce_chooses_the_grant_that_outlives_the_rootenforce, an older long grant is chosen over a newer short one, so registration succeedsinfo_advertises_the_owner_authority_mode-v1and-record, no-enforce;enforce:-v1and-enforce, no-record; noCache-Controllegacy_long_roots_are_counted_not_refusedbound/backfill; legacy and backfill counters risepost_cutover_unclassified_rows_get_the_lifetime_capunknown/lifetime-exceededunderrecordpost_cutover_rows_outliving_their_authority_are_unknown_under_enforceunknown/outlives-authorityUnit tests in
owner_authority.rs:root_lifetime_cap_boundaries_are_exact(registration + 31 d + 5 min is accepted, one second more is refused;nbf == expis accepted,nbfone second pastexpis refused) andauthority_ttl_bucket_edges_are_exact(exactly 1 h isle_1h, 1 h + 1 s isle_30d; exactly 30 d isle_30d, 30 d + 1 s isgt_30d).The first-use assertions of earlier tests wait for the background classification (
classify_on_use).sql_delegated_caveats_feature_is_advertised_only_behind_its_settingpasses as written: the owner-authority features are listed before the setting-gated ones.Notes
enforcecheck run before the idempotent re-POST shortcut, so re-posting an existing registration that breaks the current rules gets the same 403.owner_authority_expiryalongside the cutover and its source.Verification
At this head, on SQLite:
cargo fmt --all -- --check;cargo clippy --workspace --all-targetsandcargo clippy -p tinycloud-core --features duckdb --all-targets, both withRUSTFLAGS=-Dwarnings;node scripts/gen-capabilities.mjs --check; and every test binary oftinycloud-core,tinycloud-nodeandtinycloud-authpass, except the threestartup_identitycases (the host has a dstack socket, so the node binary refuses to create an identity) and the threeaccount_alias_tests, which time out when other jobs share the host and pass when thetinycloud-nodelib tests run alone. PostgreSQL-backed tests run in CI.Details: TC-1423