From 0b27178f4f07945f7dc78d971c605f2f69f106d8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 16 Oct 2026 15:53:39 +0000 Subject: [PATCH 1/2] fix(core): restore protocol Rust integration tests The official runtime grant script used psql \quit with an exit status, which psql ignores, so a refused grant exited 0 after rollback. Browser session tests then checked an unprovisioned role and failed with UnsafePrivileges. Create the required NOLOGIN installation owner first, and make refusal fail under ON_ERROR_STOP. The OAuth recovery case started on a one-second lease that CI can expire before dispatch. Start on the normal lease, then expire it explicitly. Wire sealed Core, Go host, and payment bootstrap binaries into the protocol Rust test step so the non-ignored payment recovery case runs. Co-authored-by: LIghtJUNction --- .github/workflows/core-protocol.yml | 10 ++++++++++ deployment/postgres/grant-core-runtime.sql | 6 +++++- .../core/tests/browser_sessions_independent.rs | 18 +++++++++++++++++- services/core/tests/oauth2_pi_independent.rs | 9 +++++++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/.github/workflows/core-protocol.yml b/.github/workflows/core-protocol.yml index 477afcbda..1973b34ef 100644 --- a/.github/workflows/core-protocol.yml +++ b/.github/workflows/core-protocol.yml @@ -140,6 +140,16 @@ jobs: LMM_MODEL_PROCESS_CORE_SHA256="$(sha256sum "$LMM_MODEL_PROCESS_CORE_BIN" | cut -d ' ' -f1)" LMM_MODEL_PROCESS_ROOT="$(mktemp -d /tmp/lmm-model-XXXXXXXX)" export LMM_MODEL_PROCESS_CORE_BIN LMM_MODEL_PROCESS_CORE_SHA256 LMM_MODEL_PROCESS_ROOT + # The non-ignored payment recovery case requires sealed Core, Go host + # and bootstrap binaries. The 302-second case stays ignored. + CGO_ENABLED=0 go test -C ../extensions -mod=readonly -c -o "$RUNNER_TEMP/lmm-payment-bootstrap" ./internal/modules/payments + LMM_PAYMENT_CORE_BIN="$LMM_MODEL_PROCESS_CORE_BIN" + LMM_PAYMENT_CORE_SHA256="$LMM_MODEL_PROCESS_CORE_SHA256" + LMM_PAYMENT_GO_BIN="$(realpath "$LMM_RELAY_EXTENSION_BIN")" + LMM_PAYMENT_GO_SHA256="$(sha256sum "$LMM_PAYMENT_GO_BIN" | cut -d ' ' -f1)" + LMM_PAYMENT_BOOTSTRAP_BIN="$(realpath "$RUNNER_TEMP/lmm-payment-bootstrap")" + LMM_PAYMENT_BOOTSTRAP_SHA256="$(sha256sum "$LMM_PAYMENT_BOOTSTRAP_BIN" | cut -d ' ' -f1)" + export LMM_PAYMENT_CORE_BIN LMM_PAYMENT_CORE_SHA256 LMM_PAYMENT_GO_BIN LMM_PAYMENT_GO_SHA256 LMM_PAYMENT_BOOTSTRAP_BIN LMM_PAYMENT_BOOTSTRAP_SHA256 cargo test --locked --all-targets --no-fail-fast 2>&1 | tee "$RUNNER_TEMP/core-tests.log" - name: Go contract and race tests if: ${{ !cancelled() }} diff --git a/deployment/postgres/grant-core-runtime.sql b/deployment/postgres/grant-core-runtime.sql index f5b0ce491..140f602a4 100644 --- a/deployment/postgres/grant-core-runtime.sql +++ b/deployment/postgres/grant-core-runtime.sql @@ -54,7 +54,11 @@ SELECT EXISTS ( \else \echo 'Refusing unsafe runtime role or login-enabled installation owner.' ROLLBACK; -\quit 1 +-- psql \quit ignores an exit status, so a refused script would otherwise +-- leave the process successful after this rollback. ON_ERROR_STOP is set above. +DO $$ BEGIN + RAISE EXCEPTION 'Refusing unsafe runtime role or login-enabled installation owner.'; +END $$; \endif REVOKE CREATE, TEMPORARY ON DATABASE :"core_database" FROM PUBLIC; diff --git a/services/core/tests/browser_sessions_independent.rs b/services/core/tests/browser_sessions_independent.rs index 9539725c5..03104273a 100644 --- a/services/core/tests/browser_sessions_independent.rs +++ b/services/core/tests/browser_sessions_independent.rs @@ -28,6 +28,20 @@ impl RuntimeRole { let database = owner.connect_options().get_database().unwrap().to_owned(); let mut admin = reqwest::Url::parse(&std::env::var("DATABASE_URL").unwrap()).unwrap(); assert_eq!(admin.host_str(), Some("127.0.0.1")); + // The official grant script refuses, and rolls back, unless the NOLOGIN + // installation owner already exists. That role is cluster-wide. + sqlx::raw_sql( + "DO $$ BEGIN \ + CREATE ROLE lmm_core_owner NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS; \ + EXCEPTION WHEN duplicate_object THEN \ + IF EXISTS (SELECT FROM pg_roles r WHERE r.rolname='lmm_core_owner' AND r.rolcanlogin) THEN \ + RAISE EXCEPTION 'installation owner must not log in'; \ + END IF; \ + END $$;", + ) + .execute(owner) + .await + .unwrap(); let grants = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../deployment/postgres/grant-core-runtime.sql"); let output = std::process::Command::new("/usr/bin/psql") @@ -55,7 +69,9 @@ impl RuntimeRole { .unwrap(); assert!( output.status.success(), - "official runtime grants failed before session behavior" + "official runtime grants failed before session behavior\nstdout: {}\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) ); admin.set_path(&database); admin.set_username(&name).unwrap(); diff --git a/services/core/tests/oauth2_pi_independent.rs b/services/core/tests/oauth2_pi_independent.rs index d203a76d4..acd1dac59 100644 --- a/services/core/tests/oauth2_pi_independent.rs +++ b/services/core/tests/oauth2_pi_independent.rs @@ -252,12 +252,17 @@ async fn revoked_expired_stream_retains_original_hold_until_authoritative_reconc pool: PgPool, ) { with_fixture(pool,true,|f|async move { - let token=f.token(100).await;let mut spec=f.spec("independent-crash",60);spec.lease_seconds=1;spec.maximum_seconds=1; + let token=f.token(100).await; + // A one-second lease is already stale before start once reserve and the + // following serializable transaction cross a clock second. Dispatch must + // succeed first; expiry is then the same explicit lease move used by the + // charging recovery tests. + let spec=f.spec("independent-crash",60); let held=f.reserve(&token.access_token,&spec,&[1;32]).await.unwrap(); assert!(f.charging.start_oauth(&f.issuer,&held.id,&[1;32]).await.unwrap()); f.issuer.revoke_for_client("lmm-pi",&token.access_token).await.unwrap(); sqlx::query("UPDATE core_identity.users SET active=false WHERE id=$1").bind(f.user).execute(&f.owner).await.unwrap(); - tokio::time::sleep(Duration::from_secs(2)).await; + sqlx::query("UPDATE core_charging.requests SET lease_until=floor(extract(epoch FROM clock_timestamp()))::bigint-1,revision=revision+1 WHERE id=$1").bind(&held.id).execute(&f.owner).await.unwrap(); let recovered=f.charging.recover_expired(10).await.unwrap();assert_eq!(recovered.len(),1);let recovered=&recovered[0];assert_eq!(recovered.state,"reconcile");assert_eq!(recovered.oauth_source,held.oauth_source); assert_eq!(f.ledger.balance(f.account).await.unwrap().reserved_units,60); let usage=Usage{input:25,..Default::default()}; From 51d79011aa9b2b8eb31564f199e86630ad4c70c1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 16 Oct 2026 16:01:39 +0000 Subject: [PATCH 2/2] fix(core): expire OAuth recovery leases serializably Charging updates reject read committed. The recovery test moves lease_until inside a serializable transaction after a successful start. Co-authored-by: LIghtJUNction --- services/core/tests/oauth2_pi_independent.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/core/tests/oauth2_pi_independent.rs b/services/core/tests/oauth2_pi_independent.rs index acd1dac59..a6dbfcea4 100644 --- a/services/core/tests/oauth2_pi_independent.rs +++ b/services/core/tests/oauth2_pi_independent.rs @@ -262,7 +262,10 @@ async fn revoked_expired_stream_retains_original_hold_until_authoritative_reconc assert!(f.charging.start_oauth(&f.issuer,&held.id,&[1;32]).await.unwrap()); f.issuer.revoke_for_client("lmm-pi",&token.access_token).await.unwrap(); sqlx::query("UPDATE core_identity.users SET active=false WHERE id=$1").bind(f.user).execute(&f.owner).await.unwrap(); - sqlx::query("UPDATE core_charging.requests SET lease_until=floor(extract(epoch FROM clock_timestamp()))::bigint-1,revision=revision+1 WHERE id=$1").bind(&held.id).execute(&f.owner).await.unwrap(); + let mut expiry=f.owner.begin().await.unwrap(); + sqlx::query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE").execute(&mut *expiry).await.unwrap(); + sqlx::query("UPDATE core_charging.requests SET lease_until=floor(extract(epoch FROM clock_timestamp()))::bigint-1,revision=revision+1 WHERE id=$1").bind(&held.id).execute(&mut *expiry).await.unwrap(); + expiry.commit().await.unwrap(); let recovered=f.charging.recover_expired(10).await.unwrap();assert_eq!(recovered.len(),1);let recovered=&recovered[0];assert_eq!(recovered.state,"reconcile");assert_eq!(recovered.oauth_source,held.oauth_source); assert_eq!(f.ledger.balance(f.account).await.unwrap().reserved_units,60); let usage=Usage{input:25,..Default::default()};