diff --git a/docs/changes/netkingdom-target-2026-10-18/cust80-completion-r3/completion-audit.json b/docs/changes/netkingdom-target-2026-10-18/cust80-completion-r3/completion-audit.json new file mode 100644 index 0000000..77f2b34 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/cust80-completion-r3/completion-audit.json @@ -0,0 +1,200 @@ +{ + "objective": "Finish what you found missing and finish CUST-WP-0080", + "status": "active", + "observed_at": "2026-10-18T01:27:45.628916+00:00", + "full_scope_baseline": { + "path": "docs/changes/netkingdom-target-2026-10-18/cust80-completion-r1/completion-audit.json", + "sha256": "sha256:9bdee9a790db598956fe35d5954137bb4582c9f3810d2392f7ef3236c369b065", + "all_requirements_retained": true + }, + "normative_architecture": { + "path": "docs/netkingdom-target-security-architecture.md", + "sha256": "ec2079be18c7cdb8a1e4fdbd22131c08c83721d7a1bf18b4802bc5e229520f35" + }, + "acceptance_gates": [ + { + "id": "TSA-01", + "required_outcome": "Every inventoried privileged PEP authenticates its decision", + "required_evidence": "PEP denominator; admitted signer/channel; forged, altered, wrong-key/pin, unsigned/downgrade, expired and rotation refusals. Start SECRETS-WP-0014.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-02", + "required_outcome": "Exact approval produces one bounded protected operation", + "required_evidence": "SECRETS-WP-0013/RPF-WP-0035-T12 native effect; Approval T01/T05 issuer/tenant provenance; wrong actor/tenant/body, expiry, replay and unavailable refusal.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-03", + "required_outcome": "Crashes preserve uncertainty and cleanup", + "required_evidence": "Actual ledger restart/concurrency, consumed-before-effect failure, target reconciliation, scoped cleanup and revocation evidence; no blind replay.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-04", + "required_outcome": "First reusable browser case is accepted", + "required_evidence": "NK-WP-0048 and named Mason/Warden/KeyCape adapters; independent native structure, sign-in/denial, delivery, revocation and rollback.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-05", + "required_outcome": "Human and browser consumers can use the delivered identity", + "required_evidence": "KEY-WP-0040/U08 real-factor recovery; User Engine journey; NK-WP-0050 and CUST-WP-0080-T05 human/agent Playwright acceptance.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-06", + "required_outcome": "Actual agent and runner authority is bounded", + "required_evidence": "CUST-WP-0073/GLAS-WP-0012 admitted runtime read-denial/tool-effect proof; Forge/Platform per-job credential and repository-workflow abuse tests.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-07", + "required_outcome": "Credential lifecycle is inventoried and simplified", + "required_evidence": "Current owner/consumer denominator, unknown/orphan findings, native-identity elimination candidates; sampled replacement/adoption/old-refusal and deletion semantics. G04/G09/G12.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-08", + "required_outcome": "Recovery is independent and measured", + "required_evidence": "NK-WP-0049 plus platform/root owners; isolated recovery with ordinary issuer unavailable, restore/reconnect/audit continuity and measured RTO/RPO. G08/G11.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-09", + "required_outcome": "Security effects have independently reconciled evidence", + "required_evidence": "Admitted source/class inventory, atomic outbox, receiver readback, lag/duplicate/boundary accounting and missing-class findings. Approval/Audit/Tenant/Secrets owners.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-10", + "required_outcome": "Source and delivery controls cover declared entry paths", + "required_evidence": "FORGE-WP-0006/RMGR-WP-0021: denominator, SSH/HTTP/UI/new repo/import/mirror coverage, required checks/provenance, private synthetic finding through resolution.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-11", + "required_outcome": "Discovery remains accurate", + "required_evidence": "All reviewed IDs exactly once, owner/index parity, usable source links, current limits/version aliases, drift checks and package-family disposition. Reuse/Repo Manager/provider owners.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-12", + "required_outcome": "Improvement operates without repeated manual polling", + "required_evidence": "CUST-WP-0079/Activity accepted schedule, unassisted firing, checkpoint and changed-state notifications; adopted cadence runtime/receiver receipts; owner change and native remeasurement.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-13", + "required_outcome": "Shared adapters reduce per-consumer implementation", + "required_evidence": "Producer-format cross-language fixtures, two real consumers, version compatibility, denied-path parity, upgrades and rollback; no second PDP.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-14", + "required_outcome": "Minimal profiles are independently reusable", + "required_evidence": "Version/provider lock, dependency and optional-provider declaration, provider adapters, second clean installation, complete positive/negative journeys and upgrade/recovery instructions.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-15", + "required_outcome": "Optimization improves measured operations", + "required_evidence": "Baseline and repeat measurements of onboarding time/touches, p50/p95 decision/effect latency, scanner throughput/failure, resources/headroom and owner effort with unchanged control coverage.", + "status": "incomplete", + "completion_evidence": [] + }, + { + "id": "TSA-16", + "required_outcome": "Governance and exceptions remain truthful", + "required_evidence": "Existing SEC-DSM ledger and close-down controls reconciled to exact evidence; real authority for changes, explicit residual promotions, preserved deferrals and no invented owner assent.", + "status": "incomplete", + "completion_evidence": [] + } + ], + "sec_dsm_requirements": { + "path": "docs/evidence/2026-10-12-netkingdom-security/sec-dsm-requirement-ledger.json", + "sha256": "sha256:ad81f7186ada04aa1cc410932fc8669d69dc2232f3c0be00657b17f3aa3a8184", + "count": 116, + "status_counts": { + "unverified": 84, + "incomplete": 12, + "reference": 20 + }, + "closure_status_unchanged": true + }, + "gap_ids": [ + "G01", + "G02", + "G03", + "G04", + "G05", + "G06", + "G07", + "G08", + "G09", + "G10", + "G11", + "G12", + "G13" + ], + "separate_required_deliverables": [ + "G01-G13 current closure evidence or already applicable approved exceptions", + "SEC-DSM requirement ledger reconciled with exact evidence", + "T05 real human and agent Playwright acceptance and supported discovery", + "Second real consumer and clean installation proves reuse", + "Residual handoffs before workplan finish; no required work left only in prose" + ], + "source_progress": { + "packet": "docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1", + "producer_source_commit": "git:ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3", + "consumer_owner_commit": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "consumer_paths": 40, + "patch_sha256": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c", + "postgreSQL_test_subtest_passes": 37, + "database_schema_profiles": [ + "default", + "explicit-public", + "named" + ], + "separate_client_crash_recovery": true, + "actual_database_server_restart_recovery": true, + "controlled_missing_lock_and_private_marker_counterexamples": true, + "following_query_hook_context_panic_corrected": true, + "selected_composed_passes_including_subtests": 94, + "source_skips": 0, + "release": { + "frontend_and_static_backend": "passed", + "release_version": "11.0.3+cust80.feb10793", + "binary_sha256": "sha256:92efb26614d3fca22c400920d92c4cd022e32ab86c1f562f60699ab5f750cd28", + "embedded_assets_exact": 2234, + "actual_release_CLI_checks": 48, + "exact_local_OCI_exports": 2, + "manifest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "native_admission": false + }, + "independent_owner_assent": false, + "production_database": false, + "native_gates_closed": 0 + }, + "current_work": "Current authenticated operation/inverse sessions and evidence; exclusion of all application/Helm/release writers; private journal custody/retention; ongoing HTTP/API/Git/SSH authorization; exact owner and native admission. All sixteen gates, G01-G13 and 116 SEC-DSM entries remain in scope.", + "workplan_finished": false, + "goal_complete": false, + "preceding_audit": { + "path": "docs/changes/netkingdom-target-2026-10-18/cust80-completion-r2/completion-audit.json", + "sha256": "sha256:001ede40ad6d7cadcfc575a101828f782f65ea0c75e6a5e0f6b0afe761420b0d" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/README.md b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/README.md new file mode 100644 index 0000000..830f119 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/README.md @@ -0,0 +1,88 @@ +# Forgejo PostgreSQL source verification and tracing context correction + +This packet verifies the actual composed Forgejo 11.0.3 source on a disposable +PostgreSQL 16.14 instance. It retains the source correction, failed setup and +test attempts, controlled counterexamples, exact sandbox/command receipts and +complete rebuilt release evidence. Apps commit `308e83fc` integrates the new +40-path source patch and build lock. Independent owner review and operational +admission remain open. + +The actual production engine initialization and registered model schemas run +under default, explicit-public and named schemas. OAuth configuration values use +the actual application type. All 37 PostgreSQL test/subtest results pass: +create/reconcile/inverse, exact raw restoration, unchanged source retention, +private drift refusal, local/external account-link refusal, unexpected trigger +rollback, raw SQL writer exclusion on all four protected tables, concurrent +preparation and private driver-error diagnostics. A PostgreSQL trigger places +private synthetic configuration in its driver error; the application's actual +slow/error/trace hooks and returned operation error suppress that configuration. +An unmarked query provides a positive diagnostic control. + +The diagnostics test exposed a real hook-composition crash. XORM 1.3.9 passes +the same ContextHook to each BeforeProcess hook without feeding the previous +returned context into it. A hook after tracing could therefore discard the task +context and make tracing's AfterProcess panic. The correction publishes the +new task context into ContextHook as well as returning it. This preserves the +incoming private-query marker and ordinary context values. Actual SQLite query +success/failure and private/ordinary trace cases cover the corrected composition. +The six focused DB checks and all 94 selected composed package results pass +without skips. The test-only PostgreSQL tag adds no runtime route or authority. + +Separate client processes prepare an intent, die immediately after the actual +source INSERT, reconcile the rolled-back prepared state, and confirm an apply. +The actual PostgreSQL server then restarts with the same synthetic volume. +Further fresh processes reconcile the committed apply, perform the inverse and +verify the reverted journal and source absence. This is transaction/recovery +mechanics evidence, not a real initiating principal, current inverse authority, +signed evidence/outbox or a deployed-database acceptance receipt. + +Controlled source copies independently remove the PostgreSQL table lock and +the private-query marker. Both fail their intended tests in all three schema +profiles. Those expected counterexamples are separate from successful normal +runs; the canonical runtime source is not weakened. See +`controlled-counterexamples.json` and `source-equivalence.json`. + +The first two container setup attempts failed because the image's initialization +script explicitly clears PGHOST and requires its default socket. The first +suite also exposed an incomplete user fixture; its replacement uses the actual +user model. The initial tracing regression had an incorrect AddHook API call. +The first combined run reused an already-consumed file-replacement fixture; +the final run provisions fresh read-only and writable-alias fixtures. All +original failures remain retained. None is counted as a source success. + +The database has no TCP listener, published port or network. Only its exact +synthetic Unix socket is exposed to the network/PID-isolated test process. +The image is immutable and recorded; the workload is limited to one CPU and +512 MiB. Local trust authentication is solely a disposable fixture, not a +credential-custody proof. Exact labeled containers and volumes from all three +setup attempts were removed; cleanup receipts are retained. + +For reproduction, use a new prepared workspace from the companion release +recipe, explicit public module/build caches, and unused attempt/workspace names. +`database_runtime.py start --attempt N` creates the owned database fixture. +`run_postgres.py build --source-workspace ... --module-cache ... --go-cache ...` +compiles the real test binary, and `suite` / `combined-regressions` execute the +normal checks. Supply the same `--runtime-attempt N` and test `--workspace`. +The recovery actions are `prepare`, `crash` (expected process exit 73), +`recover-prepared`, server `restart`, `recover-applied`, and `recover-inverse`. +The optional build controls are `omit-postgres-lock` and `omit-private-context`; +their suite failures must match the recorded target tests. Finish with exact +owned `database_runtime.py cleanup --attempt N`. Existing receipts and trial +directories refuse overwrite. The early fixed-path receipt replay is retained; +the final combined run exercises the explicit-workspace interface. + +The new patch independently replays all 40 postimages from the pinned official +archive. The complete frontend/static release builds; all 2,234 embedded assets +are exact and all 48 actual CLI/SQLite subprocess checks pass on its binary. +Two fresh no-cache local OCI exports reproduce identical archive, manifest, +config and index. Base layers and rootless runtime settings remain exact. +This is one source build and two exports. No image is loaded, pushed or deployed. +Published asset digests have explicit `sha256:` types; their derivation restores +the runtime's original manifest byte for byte. Scanner rules are unchanged. + +Existing T08/T34/T42 still require current authenticated operation/inverse +sessions, exclusion of every application/Helm/release writer, private journal +reader/backup custody, retention and native deployed-database acceptance. T07's +chart/private-mount admission and T09/T43's ongoing HTTP/API/Git/SSH authorization +remain open. All sixteen target gates and the full SEC-DSM ledger remain in +scope; no native gate is closed by these source checks. diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/annotate_embedded_manifest.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/annotate_embedded_manifest.py new file mode 100644 index 0000000..d58a6f4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/annotate_embedded_manifest.py @@ -0,0 +1,36 @@ +"""Publish explicitly typed public asset digests, retaining byte-exact evidence.""" +from pathlib import Path +import hashlib +import json + +HERE = Path(__file__).resolve().parent +path = HERE / 'release-evidence/embedded-files.json' +raw = path.read_bytes() +records = json.loads(raw) +work = Path('/tmp/custodian-forgejo-release-r4') +verification = json.loads((HERE / 'release-evidence/verification.json').read_text()) +sha = lambda data: hashlib.sha256(data).hexdigest() +assert sha(raw) == verification['embedded_file_manifest_sha256'] +assert raw == (work / 'embedded-files.json').read_bytes() +for name, value in records.items(): + assert sha((work / 'source' / name).read_bytes()) == value, name + assert sha((work / 'embedded-extracted' / name).read_bytes()) == value, name +published = (json.dumps({name: 'sha256:' + value for name, value in records.items()}, + indent=2, sort_keys=True) + '\n').encode() +restored = (json.dumps({name: value.removeprefix('sha256:') + for name, value in json.loads(published).items()}, + indent=2, sort_keys=True) + '\n').encode() +assert restored == raw +path.write_bytes(published) +(HERE / 'release-evidence/embedded-manifest-derivation.json').write_text(json.dumps({ + 'scope': 'Public generated/embedded asset SHA-256 identifiers only', + 'assets_verified_against_both_actual_source_and_extracted_binary': len(records), + 'runtime_original_manifest_sha256': 'sha256:' + sha(raw), + 'published_manifest_sha256': 'sha256:' + sha(published), + 'original_byte_exact_reconstructed': True, + 'reconstruction': 'Remove the sha256: value prefix and serialize with json.dumps(indent=2, sort_keys=True) plus one newline.', + 'runtime_verification_receipt_preserved': True, + 'scanner_rules_or_allowlists_changed': False, + 'credential_values_transformed': False, +}, indent=2) + '\n') +print(json.dumps({'public_asset_digests_verified': len(records), 'original_byte_exact_reconstructed': True})) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-forgejo-source.patch b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-forgejo-source.patch new file mode 100644 index 0000000..9465e3f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-forgejo-source.patch @@ -0,0 +1,5001 @@ +--- a/cmd/admin_auth_oauth.go ++++ b/cmd/admin_auth_oauth.go +@@ -9,6 +9,7 @@ + "net/url" + + auth_model "forgejo.org/models/auth" ++ "forgejo.org/models/db" + "forgejo.org/services/auth/source/oauth2" + + "github.com/urfave/cli/v2" +@@ -35,6 +36,10 @@ + Name: "secret", + Value: "", + Usage: "Client Secret", ++ }, ++ &cli.StringFlag{ ++ Name: "secret-file", ++ Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file", + }, + &cli.StringFlag{ + Name: "auto-discover-url", +@@ -94,6 +99,10 @@ + Name: "required-claim-value", + Value: "", + Usage: "Claim value that has to be set to allow users to login with this source", ++ }, ++ &cli.StringSliceFlag{ ++ Name: "required-claim-values", ++ Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)", + }, + &cli.StringFlag{ + Name: "group-claim-name", +@@ -160,6 +169,7 @@ + Scopes: c.StringSlice("scopes"), + RequiredClaimName: c.String("required-claim-name"), + RequiredClaimValue: c.String("required-claim-value"), ++ RequiredClaimValues: c.StringSlice("required-claim-values"), + GroupClaimName: c.String("group-claim-name"), + AdminGroup: c.String("admin-group"), + RestrictedGroup: c.String("restricted-group"), +@@ -169,14 +179,31 @@ + } + + func runAddOauth(c *cli.Context) error { ++ secret, selected, err := oauthSecretInput(c) ++ if err != nil { ++ return err ++ } + ctx, cancel := installSignals() + defer cancel() ++ ctx = db.WithSensitiveSQL(ctx) + + if err := initDB(ctx); err != nil { + return err + } ++ if db.SQLLoggingEnabled(ctx) { ++ return errors.New("OAuth configuration requires SQL logging disabled") ++ } + + config := parseOAuth2Config(c) ++ if selected { ++ config.ClientSecret = secret ++ } ++ if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil { ++ return err ++ } ++ if config.RequiredClaimValues != nil && config.Provider != "openidConnect" { ++ return errors.New("required-claim-values requires openidConnect") ++ } + if config.Provider == "openidConnect" { + discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL) + if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { +@@ -184,24 +211,36 @@ + } + } + +- return auth_model.CreateSource(ctx, &auth_model.Source{ ++ err = auth_model.CreateSource(ctx, &auth_model.Source{ + Type: auth_model.OAuth2, + Name: c.String("name"), + IsActive: true, + Cfg: config, + }) ++ if err != nil && c.IsSet("secret-file") { ++ return errors.New("OAuth source creation failed") ++ } ++ return err + } + + func runUpdateOauth(c *cli.Context) error { ++ secret, selected, err := oauthSecretInput(c) ++ if err != nil { ++ return err ++ } + if !c.IsSet("id") { + return errors.New("--id flag is missing") + } + + ctx, cancel := installSignals() + defer cancel() ++ ctx = db.WithSensitiveSQL(ctx) + + if err := initDB(ctx); err != nil { + return err ++ } ++ if db.SQLLoggingEnabled(ctx) { ++ return errors.New("OAuth configuration requires SQL logging disabled") + } + + source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) +@@ -209,7 +248,10 @@ + return err + } + +- oAuth2Config := source.Cfg.(*oauth2.Source) ++ oAuth2Config, ok := source.Cfg.(*oauth2.Source) ++ if !ok { ++ return errors.New("authentication source is not OAuth2") ++ } + + if c.IsSet("name") { + source.Name = c.String("name") +@@ -223,8 +265,8 @@ + oAuth2Config.ClientID = c.String("key") + } + +- if c.IsSet("secret") { +- oAuth2Config.ClientSecret = c.String("secret") ++ if selected { ++ oAuth2Config.ClientSecret = secret + } + + if c.IsSet("auto-discover-url") { +@@ -244,6 +286,10 @@ + } + if c.IsSet("required-claim-value") { + oAuth2Config.RequiredClaimValue = c.String("required-claim-value") ++ } ++ ++ if c.IsSet("required-claim-values") { ++ oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values") + } + + if c.IsSet("group-claim-name") { +@@ -293,7 +339,30 @@ + } + + oAuth2Config.CustomURLMapping = customURLMapping ++ if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil { ++ return err ++ } ++ if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" { ++ return errors.New("required-claim-values requires openidConnect") ++ } + source.Cfg = oAuth2Config + +- return auth_model.UpdateSource(ctx, source) ++ err = auth_model.UpdateSource(ctx, source) ++ if err != nil && c.IsSet("secret-file") { ++ return errors.New("OAuth source update failed") ++ } ++ return err + } ++ ++// Resolve before opening the DB or mutating a loaded source. Never store the ++// resolved bytes in the CLI context: they must not become flag/argv diagnostics. ++func oauthSecretInput(c *cli.Context) (string, bool, error) { ++ if c.IsSet("secret-file") { ++ if c.IsSet("secret") { ++ return "", false, errors.New("secret and secret-file are mutually exclusive") ++ } ++ value, err := readOAuthSecretFile(c.String("secret-file")) ++ return value, true, err ++ } ++ return c.String("secret"), c.IsSet("secret"), nil ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_linux.go +@@ -0,0 +1,114 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build linux ++ ++package cmd ++ ++import ( ++ "errors" ++ "io" ++ "os" ++ "path/filepath" ++ "strings" ++ ++ "golang.org/x/sys/unix" ++) ++ ++var errOAuthSecretFile = errors.New("secret-file input refused") ++ ++// This checks DAC and mount properties, not namespace, Secret provenance or ++// operation authority. Admission must bind the exact workload/mount/reader set. ++// Kubernetes Secret subPath leaves are root-owned; fsGroup can provide 0440 ++// access. Requiring euid ownership would incorrectly refuse that supported case. ++func readOAuthSecretFile(path string) (string, error) { ++ if len(path) > 4096 || !filepath.IsAbs(path) || filepath.Clean(path) != path || strings.ContainsRune(path, 0) { ++ return "", errOAuthSecretFile ++ } ++ parts := strings.Split(strings.TrimPrefix(path, "/"), "/") ++ if len(parts) < 2 { ++ return "", errOAuthSecretFile ++ } ++ dir, err := unix.Open("/", unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ defer func() { unix.Close(dir) }() ++ for _, part := range parts[:len(parts)-1] { ++ var st unix.Stat_t ++ if unix.Fstat(dir, &st) != nil || !safeOAuthSecretParent(st) { ++ return "", errOAuthSecretFile ++ } ++ next, err := unix.Openat(dir, part, unix.O_PATH|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ unix.Close(dir) ++ dir = next ++ } ++ var parent unix.Stat_t ++ if unix.Fstat(dir, &parent) != nil || !safeOAuthSecretParent(parent) { ++ return "", errOAuthSecretFile ++ } ++ // Nonblocking is necessary: reject a FIFO without waiting for a writer. ++ fd, err := unix.Openat(dir, parts[len(parts)-1], unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_NONBLOCK|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ f := os.NewFile(uintptr(fd), "private OAuth input") ++ defer f.Close() ++ return readOAuthSecretFD(f, dir, parts[len(parts)-1]) ++} ++ ++func readOAuthSecretFD(f *os.File, dir int, leaf string) (string, error) { ++ fd := int(f.Fd()) ++ var before, after, named unix.Stat_t ++ var mount unix.Statfs_t ++ if unix.Fstat(fd, &before) != nil || !safeOAuthSecretLeaf(before) || unix.Fstatfs(fd, &mount) != nil || mount.Flags&unix.ST_RDONLY == 0 { ++ return "", errOAuthSecretFile ++ } ++ // mint-secret uses secrets.token_urlsafe(48): exactly 64 ASCII characters, ++ // no padding/newline. Bounded read still detects a concurrent size increase. ++ data, err := io.ReadAll(io.LimitReader(f, 65)) ++ defer clear(data) ++ if err != nil || len(data) != 64 { ++ return "", errOAuthSecretFile ++ } ++ for _, b := range data { ++ if !(b >= 'A' && b <= 'Z' || b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '_') { ++ return "", errOAuthSecretFile ++ } ++ } ++ if unix.Fstat(fd, &after) != nil || !sameOAuthSecretFile(before, after) || unix.Fstatat(dir, leaf, &named, unix.AT_SYMLINK_NOFOLLOW) != nil || !sameOAuthSecretFile(before, named) { ++ return "", errOAuthSecretFile ++ } ++ return string(data), nil ++} ++ ++func safeOAuthSecretParent(st unix.Stat_t) bool { ++ return st.Mode&unix.S_IFMT == unix.S_IFDIR && (st.Uid == 0 || st.Uid == uint32(os.Geteuid())) && st.Mode&0022 == 0 ++} ++ ++func safeOAuthSecretLeaf(st unix.Stat_t) bool { ++ if st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 || st.Size != 64 || st.Mode&07337 != 0 || (st.Uid != 0 && st.Uid != uint32(os.Geteuid())) { ++ return false ++ } ++ if st.Mode&0040 != 0 { ++ groups, err := os.Getgroups() ++ if err != nil { ++ return false ++ } ++ member := st.Gid == uint32(os.Getegid()) ++ for _, gid := range groups { ++ member = member || st.Gid == uint32(gid) ++ } ++ if !member { ++ return false ++ } ++ } ++ return true ++} ++ ++func sameOAuthSecretFile(a, b unix.Stat_t) bool { ++ return a.Dev == b.Dev && a.Ino == b.Ino && a.Mode == b.Mode && a.Nlink == b.Nlink && a.Uid == b.Uid && a.Gid == b.Gid && a.Size == b.Size && a.Mtim == b.Mtim && a.Ctim == b.Ctim ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_linux_test.go +@@ -0,0 +1,79 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build linux ++ ++package cmd ++ ++import ( ++ "golang.org/x/sys/unix" ++ "os" ++ "testing" ++) ++ ++func TestPrivateSecretMetadata(t *testing.T) { ++ st := unix.Stat_t{Mode: unix.S_IFREG | 0440, Uid: 0, Gid: uint32(os.Getegid()), Nlink: 1, Size: 64} ++ if !safeOAuthSecretLeaf(st) { ++ t.Fatal("root-owned fsGroup-readable Secret subPath refused") ++ } ++ for _, mode := range []uint32{0444, 0640, 0540, 0460, 0441, 0440 | unix.S_ISUID, 0440 | unix.S_ISGID, 0440 | unix.S_ISVTX} { ++ x := st ++ x.Mode = unix.S_IFREG | mode ++ if safeOAuthSecretLeaf(x) { ++ t.Fatalf("unsafe mode accepted: %o", mode) ++ } ++ } ++ for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Uid = 123456 }, func(x *unix.Stat_t) { x.Nlink = 2 }, func(x *unix.Stat_t) { x.Size = 63 }, func(x *unix.Stat_t) { x.Mode = unix.S_IFIFO | 0400 }, func(x *unix.Stat_t) { x.Gid = 123456 }} { ++ x := st ++ mutate(&x) ++ if safeOAuthSecretLeaf(x) { ++ t.Fatal("unsafe metadata accepted") ++ } ++ } ++ if !safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0755, Uid: 0}) { ++ t.Fatal("root parent refused") ++ } ++ if safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0775, Uid: 0}) { ++ t.Fatal("group writable parent accepted") ++ } ++ for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Dev++ }, func(x *unix.Stat_t) { x.Ino++ }, func(x *unix.Stat_t) { x.Ctim.Nsec++ }, func(x *unix.Stat_t) { x.Mtim.Nsec++ }, func(x *unix.Stat_t) { x.Size++ }, func(x *unix.Stat_t) { x.Uid++ }, func(x *unix.Stat_t) { x.Gid++ }, func(x *unix.Stat_t) { x.Nlink++ }, func(x *unix.Stat_t) { x.Mode++ }} { ++ x := st ++ mutate(&x) ++ if sameOAuthSecretFile(st, x) { ++ t.Fatal("changed file considered stable") ++ } ++ } ++ x := st ++ x.Atim.Nsec++ ++ if !sameOAuthSecretFile(st, x) { ++ t.Fatal("read-induced atime change refused") ++ } ++} ++ ++// Actual Linux descriptors and namespace read-only bind mounts, not mocked stat ++// metadata. A writable alias lets the fixture simulate replacement after open. ++func TestPrivateSecretOpenedFileReplacement(t *testing.T) { ++ if _, err := os.Stat("/run/forgejo-oidc/race"); os.IsNotExist(err) { ++ t.Skip("requires isolated read-only bind fixture") ++ } ++ dir, err := unix.Open("/run/forgejo-oidc", unix.O_PATH|unix.O_DIRECTORY, 0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer unix.Close(dir) ++ f, err := os.Open("/run/forgejo-oidc/race") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer f.Close() ++ // Preserve the opened inode with one link; replace only the named leaf. ++ if err := os.Rename("/run/writable-input/race", "/run/writable-input/race-backup"); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.Rename("/run/writable-input/race-replacement", "/run/writable-input/race"); err != nil { ++ t.Fatal(err) ++ } ++ if value, err := readOAuthSecretFD(f, dir, "race"); err != errOAuthSecretFile || value != "" { ++ t.Fatal("replaced leaf accepted") ++ } ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_other.go +@@ -0,0 +1,12 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build !linux ++ ++package cmd ++ ++import "errors" ++ ++func readOAuthSecretFile(string) (string, error) { ++ return "", errors.New("secret-file input refused") ++} +--- a/go.mod ++++ b/go.mod +@@ -255,3 +255,7 @@ + replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 + + replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 ++ ++require github.com/netkingdom/access-engine v0.0.0 ++ ++replace github.com/netkingdom/access-engine => ./third_party/access-engine-pep +--- /dev/null ++++ b/models/auth/source_operation.go +@@ -0,0 +1,430 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package auth ++ ++// This owner-confined database primitive is NOT an authorization boundary. ++// No CLI/web/API route exposes it. The admitted runtime must separately obtain ++// fresh authenticated apply/inverse sessions and retain signed public evidence. ++// Configurations and private journal rows must never leave database custody. ++ ++import ( ++ "context" ++ "encoding/json" ++ "errors" ++ "reflect" ++ "regexp" ++ "sort" ++ ++ "forgejo.org/models/db" ++ "forgejo.org/modules/setting" ++ "forgejo.org/modules/timeutil" ++ "xorm.io/xorm" ++) ++ ++var ( ++ ErrSourceOperationConflict = errors.New("source operation requires owner reconciliation") ++ ErrSourceOperationStorage = errors.New("source operation storage failure; reconcile before retry") ++ ErrSourceOperationUnsupported = errors.New("source operation requires standalone SQLite/PostgreSQL transaction with SQL logging disabled") ++ operationIDPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._:-]{7,127}$`) ++) ++ ++// Private source rows deliberately bypass Config decoding: decoding and ++// reserializing can erase unknown fields, including a gate on an older image. ++type sourceOperationRow struct { ++ ID int64 `xorm:"pk autoincr"` ++ Type Type ++ Name string ++ IsActive bool ++ IsSyncEnabled bool ++ Cfg string `xorm:"TEXT"` ++ CreatedUnix timeutil.TimeStamp ++ UpdatedUnix timeutil.TimeStamp ++} ++ ++func (sourceOperationRow) TableName() string { return "login_source" } ++ ++// The whole inventory, including private configs, stays in the owner's database. ++// Neither journal payload nor its digest is a public result. ++type sourceOperationJournal struct { ++ OperationID string `xorm:"pk VARCHAR(128)"` ++ CaseID string `xorm:"VARCHAR(128)"` ++ TargetName string ++ TargetID int64 ++ Phase string ++ BeforeState string `xorm:"TEXT"` ++ DesiredState string `xorm:"TEXT"` ++ PostState string `xorm:"TEXT"` ++} ++ ++func (sourceOperationJournal) TableName() string { return "login_source_operation" } ++func init() { db.RegisterModel(new(sourceOperationJournal)) } ++ ++// SourceOperationResult contains only public outcome metadata. An authenticated ++// session/evidence producer must bind this result to its actual principal, ++// admitted source revision, exact parameters and current operation session. ++type SourceOperationResult struct { ++ OperationID string ++ Phase string ++ SourceID int64 ++} ++ ++// sourceOperationTx acquires a database-enforced lock before any source read. ++// SQLite's first write serializes every database writer, including stock admin ++// CRUD. PostgreSQL table locks exclude ordinary SQL source and user-link writes. ++// Privileged DDL, process/provider refresh and release writers are outside this ++// bounded primitive; they still require owner runtime/release exclusion. ++func sourceOperationTx(ctx context.Context, f func(context.Context) error) error { ++ if db.InTransaction(ctx) || setting.Database.LogSQL || (!setting.Database.Type.IsSQLite3() && !setting.Database.Type.IsPostgreSQL()) { ++ return ErrSourceOperationUnsupported ++ } ++ err := db.WithTx(db.WithSensitiveSQL(ctx), func(tx context.Context) error { ++ session, ok := db.GetEngine(tx).(*xorm.Session) ++ if !ok || session.Engine().Logger().IsShowSQL() { ++ return ErrSourceOperationUnsupported ++ } ++ if setting.Database.Type.IsPostgreSQL() { ++ if _, err := db.GetEngine(tx).Exec(`LOCK TABLE login_source_operation, login_source, "user", external_login_user IN EXCLUSIVE MODE`); err != nil { ++ return err ++ } ++ } else { ++ // Even with no matching row, UPDATE starts SQLite's write transaction. ++ if _, err := db.GetEngine(tx).Exec(`UPDATE login_source_operation SET phase = phase WHERE operation_id = ''`); err != nil { ++ return err ++ } ++ } ++ return f(tx) ++ }) ++ if err == nil || errors.Is(err, ErrSourceOperationConflict) || errors.Is(err, ErrSourceOperationUnsupported) || IsErrSourceInUse(err) { ++ return err ++ } ++ return ErrSourceOperationStorage ++} ++ ++func sourceInventory(ctx context.Context) ([]sourceOperationRow, error) { ++ rows := make([]sourceOperationRow, 0) ++ err := db.GetEngine(ctx).OrderBy("id").Find(&rows) ++ return rows, err ++} ++func encodeSourceInventory(rows []sourceOperationRow) string { ++ b, _ := json.Marshal(rows) ++ return string(b) ++} ++func decodeSourceInventory(s string) ([]sourceOperationRow, error) { ++ var rows []sourceOperationRow ++ if err := json.Unmarshal([]byte(s), &rows); err != nil { ++ return nil, ErrSourceOperationConflict ++ } ++ return rows, nil ++} ++func sourceJournal(ctx context.Context, operationID string) (*sourceOperationJournal, error) { ++ row := new(sourceOperationJournal) ++ found, err := db.GetEngine(ctx).ID(operationID).Get(row) ++ if err != nil { ++ return nil, err ++ } ++ if !found { ++ return nil, ErrSourceOperationConflict ++ } ++ return row, nil ++} ++func journalResult(j *sourceOperationJournal) SourceOperationResult { ++ return SourceOperationResult{OperationID: j.OperationID, Phase: j.Phase, SourceID: j.TargetID} ++} ++ ++// PrepareSourceOperation durably captures exact source identity and complete ++// original inventory before mutation. desired is private owner input, not an ++// authorization token. targetID=0 requires authoritative name absence. Existing ++// sources require both exact ID and name. Reuse of an operation ID is refused. ++func PrepareSourceOperation(ctx context.Context, operationID, caseID string, targetID int64, desired *Source) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ if !operationIDPattern.MatchString(operationID) || !operationIDPattern.MatchString(caseID) || desired == nil || desired.Type != OAuth2 || desired.Name == "" || desired.Cfg == nil || desired.IsSyncEnabled || desired.ID != targetID { ++ return result, ErrSourceOperationConflict ++ } ++ cfg, err := desired.Cfg.ToDB() ++ if err != nil || len(cfg) > 64*1024 || !json.Valid(cfg) { ++ return result, ErrSourceOperationConflict ++ } ++ candidate := sourceOperationRow{ID: targetID, Type: OAuth2, Name: desired.Name, IsActive: desired.IsActive, Cfg: string(cfg)} ++ candidateBytes, _ := json.Marshal(candidate) ++ err = sourceOperationTx(ctx, func(tx context.Context) error { ++ // Bound confidential duplicate custody. Refuse rather than erase journals ++ // with pending recovery or silently expire terminal private snapshots. ++ count, err := db.GetEngine(tx).Count(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if count >= 256 { ++ return ErrSourceOperationConflict ++ } ++ active, err := db.GetEngine(tx).Where("phase IN (?, ?, ?)", "prepared", "applied", "unchanged").Count(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if active >= 64 { ++ return ErrSourceOperationConflict ++ } ++ found, err := db.GetEngine(tx).ID(operationID).Exist(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if found { ++ return ErrSourceOperationConflict ++ } ++ shared, err := db.GetEngine(tx).Where("target_name = ? AND phase IN (?, ?, ?)", desired.Name, "prepared", "applied", "unchanged").Exist(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if shared { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if len(encodeSourceInventory(rows)) > 1024*1024 { ++ return ErrSourceOperationConflict ++ } ++ matched := false ++ for _, row := range rows { ++ if row.Name == desired.Name || row.ID == targetID { ++ if targetID == 0 || row.ID != targetID || row.Name != desired.Name || row.Type != OAuth2 { ++ return ErrSourceOperationConflict ++ } ++ matched = true ++ } ++ } ++ if targetID != 0 && !matched { ++ return ErrSourceOperationConflict ++ } ++ j := &sourceOperationJournal{OperationID: operationID, CaseID: caseID, TargetName: desired.Name, TargetID: targetID, Phase: "prepared", BeforeState: encodeSourceInventory(rows), DesiredState: string(candidateBytes)} ++ if _, err := db.GetEngine(tx).Insert(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// ApplySourceOperation atomically compares every original raw source row, ++// mutates exactly the target and persists confirmed post-state. A lost commit ++// acknowledgement is reconciled using the same operation ID, never a fresh ID. ++// No source registration/network call occurs inside the database transaction. ++func ApplySourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if j.Phase == "applied" || j.Phase == "unchanged" { ++ if encodeSourceInventory(rows) != j.PostState { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ } ++ if j.Phase != "prepared" || encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ var desired sourceOperationRow ++ if json.Unmarshal([]byte(j.DesiredState), &desired) != nil { ++ return ErrSourceOperationConflict ++ } ++ unchanged := false ++ for _, row := range rows { ++ if row.ID == j.TargetID { ++ desired.CreatedUnix = row.CreatedUnix ++ desired.UpdatedUnix = row.UpdatedUnix ++ unchanged = reflect.DeepEqual(row, desired) ++ } ++ } ++ if !unchanged { ++ desired.UpdatedUnix = timeutil.TimeStampNow() ++ if j.TargetID == 0 { ++ desired.CreatedUnix = desired.UpdatedUnix ++ if _, err := db.GetEngine(tx).NoAutoTime().Insert(&desired); err != nil { ++ return err ++ } ++ j.TargetID = desired.ID ++ } else { ++ n, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(&desired) ++ if err != nil { ++ return err ++ } ++ if n != 1 { ++ return ErrSourceOperationConflict ++ } ++ } ++ } ++ post, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ expected := append([]sourceOperationRow{}, rows...) ++ if !unchanged { ++ replaced := false ++ for i := range expected { ++ if expected[i].ID == desired.ID { ++ expected[i] = desired ++ replaced = true ++ } ++ } ++ if !replaced { ++ expected = append(expected, desired) ++ } ++ sort.Slice(expected, func(i, k int) bool { return expected[i].ID < expected[k].ID }) ++ } ++ if !reflect.DeepEqual(post, expected) { ++ return ErrSourceOperationConflict ++ } ++ j.PostState = encodeSourceInventory(post) ++ j.Phase = "applied" ++ if unchanged { ++ j.Phase = "unchanged" ++ } ++ if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// InverseSourceOperation requires fresh external inverse authority on EACH call. ++// It restores a preexisting source in place; only confirmed case creation can ++// delete. Any linked local/external account refuses deletion without alteration. ++func InverseSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if j.Phase == "reverted" || j.Phase == "cancelled" { ++ if encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ } ++ if j.Phase == "prepared" { ++ if encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ j.Phase = "cancelled" ++ } else { ++ if (j.Phase != "applied" && j.Phase != "unchanged") || encodeSourceInventory(rows) != j.PostState { ++ return ErrSourceOperationConflict ++ } ++ before, err := decodeSourceInventory(j.BeforeState) ++ if err != nil { ++ return err ++ } ++ var original *sourceOperationRow ++ for i := range before { ++ if before[i].ID == j.TargetID { ++ original = &before[i] ++ } ++ } ++ if original != nil { ++ if _, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(original); err != nil { ++ return err ++ } ++ } else { ++ for _, table := range []struct{ name, column string }{{"user", "login_source"}, {"external_login_user", "login_source_id"}} { ++ count, err := db.GetEngine(tx).Table(table.name).Where(table.column+" = ?", j.TargetID).Count() ++ if err != nil { ++ return err ++ } ++ if count != 0 { ++ return ErrSourceInUse{ID: j.TargetID} ++ } ++ } ++ n, err := db.GetEngine(tx).ID(j.TargetID).Delete(new(sourceOperationRow)) ++ if err != nil { ++ return err ++ } ++ if n != 1 { ++ return ErrSourceOperationConflict ++ } ++ } ++ restored, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if encodeSourceInventory(restored) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ j.Phase = "reverted" ++ } ++ if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// ReconcileSourceOperation only checks committed journal/current equality. It ++// never infers that a prepared intent ran and never retries the mutation. The ++// separately authenticated runtime must call this after an ambiguous result. ++func ReconcileSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ expected := j.BeforeState ++ switch j.Phase { ++ case "prepared", "reverted", "cancelled": ++ case "applied", "unchanged": ++ expected = j.PostState ++ default: ++ return ErrSourceOperationConflict ++ } ++ if encodeSourceInventory(rows) != expected { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} +--- /dev/null ++++ b/models/auth/source_operation_internal_test.go +@@ -0,0 +1,87 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth ++ ++import ( ++ "context" ++ "testing" ++ "time" ++ ++ "forgejo.org/models/db" ++ "xorm.io/xorm" ++) ++ ++func TestSourceOperationDatabaseWriterExclusion(t *testing.T) { ++ // Use DEFERRED transaction mode in the managed engine as well, so this ++ // test detects removal of the primitive's explicit first write. ++ previousContext := db.DefaultContext ++ previousEngine, ok := db.GetEngine(previousContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual fixture engine") ++ } ++ managed, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred") ++ if err != nil { ++ t.Fatal(err) ++ } ++ db.SetDefaultEngine(context.Background(), managed) ++ defer func() { db.SetDefaultEngine(previousContext, previousEngine); managed.Close() }() ++ // A second actual connection executes uncooperative raw SQL, so this checks ++ // database exclusion rather than an advisory-lock convention. ++ raw, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer raw.Close() ++ locked := make(chan struct{}) ++ release := make(chan struct{}) ++ done := make(chan error, 1) ++ go func() { ++ done <- sourceOperationTx(db.DefaultContext, func(ctx context.Context) error { close(locked); <-release; return nil }) ++ }() ++ select { ++ case <-locked: ++ case err := <-done: ++ t.Fatalf("lock failed: %v", err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("lock timed out") ++ } ++ writeDone := make(chan error, 1) ++ go func() { _, err := raw.Exec("UPDATE login_source SET name = name WHERE id = -123"); writeDone <- err }() ++ select { ++ case err := <-writeDone: ++ close(release) ++ <-done ++ t.Fatalf("uncooperative write escaped exclusion: %v", err) ++ case <-time.After(100 * time.Millisecond): ++ } ++ close(release) ++ select { ++ case err := <-done: ++ if err != nil { ++ t.Fatal(err) ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("transaction did not finish") ++ } ++ select { ++ case err := <-writeDone: ++ if err != nil { ++ t.Fatal(err) ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("writer did not resume") ++ } ++} ++func TestSourceOperationRefusesActualXORMArgumentLogging(t *testing.T) { ++ engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual engine") ++ } ++ engine.ShowSQL(true) ++ defer engine.ShowSQL(false) ++ called := false ++ err := sourceOperationTx(db.DefaultContext, func(context.Context) error { called = true; return nil }) ++ if err != ErrSourceOperationUnsupported || called { ++ t.Fatal("actual SQL argument logger did not fail closed") ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_postgres_export_test.go +@@ -0,0 +1,7 @@ ++//go:build source_operation_postgres ++ ++package auth ++ ++// Test-only seam: the production transaction body still obtains its real locks ++// and private query context. No runtime API or authority callback is added. ++var SourceOperationPostgresTransactionForTest = sourceOperationTx +--- /dev/null ++++ b/models/auth/source_operation_postgres_test.go +@@ -0,0 +1,369 @@ ++//go:build source_operation_postgres ++ ++package auth_test ++ ++import ( ++ "bytes" ++ "context" ++ "errors" ++ "fmt" ++ "os" ++ "runtime/trace" ++ "strings" ++ "sync" ++ "testing" ++ "time" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ user_model "forgejo.org/models/user" ++ "forgejo.org/modules/log" ++ "forgejo.org/modules/setting" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/lib/pq" ++ "github.com/stretchr/testify/require" ++ "xorm.io/xorm" ++) ++ ++type postgresOperationLog struct { ++ log.Logger ++ mu sync.Mutex ++ entries []string ++} ++ ++func (l *postgresOperationLog) Log(_ int, _ log.Level, format string, values ...any) { ++ l.mu.Lock() ++ defer l.mu.Unlock() ++ l.entries = append(l.entries, fmt.Sprintf(format, values...)) ++} ++func (l *postgresOperationLog) take() string { ++ l.mu.Lock() ++ defer l.mu.Unlock() ++ result := strings.Join(l.entries, "\n") ++ l.entries = nil ++ return result ++} ++ ++// The real Forgejo engine/driver, registered model schemas and public operation ++// functions run. Only the database, application configuration and actors are ++// synthetic. No deployed owner database or authenticated operation is claimed. ++func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) { ++ t.Helper() ++ require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required") ++ oldSettings, oldContext := setting.Database, db.DefaultContext ++ oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine) ++ require.True(t, ok) ++ expectedSchema := schema ++ if expectedSchema == "" { ++ expectedSchema = "public" ++ } ++ raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+expectedSchema) ++ require.NoError(t, err) ++ _, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + expectedSchema + `"`) ++ require.NoError(t, err) ++ setting.Database.Type = "postgres" ++ setting.Database.Host = "/run/source-operation-postgres" ++ setting.Database.Name = "custodian_source" ++ setting.Database.User = "custodian_test" ++ setting.Database.Passwd = "" ++ setting.Database.Schema = schema ++ setting.Database.SSLMode = "disable" ++ setting.Database.LogSQL = false ++ setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2 ++ setting.Database.SlowQueryThreshold = 0 ++ require.NoError(t, db.InitEngine(context.Background())) ++ engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) ++ require.True(t, ok) ++ t.Cleanup(func() { ++ _ = engine.Close() ++ _ = raw.Close() ++ setting.Database = oldSettings ++ db.SetDefaultEngine(oldContext, oldEngine) ++ }) ++ version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema") ++ require.NoError(t, err) ++ require.Equal(t, "16", string(version[0]["major"])) ++ require.Equal(t, expectedSchema, string(version[0]["schema"])) ++ require.NoError(t, db.SyncAllTables()) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ return db.DefaultContext, engine, raw ++} ++ ++func resetPostgresOperations(t *testing.T, engine *xorm.Engine) { ++ t.Helper() ++ _, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`) ++ require.NoError(t, err) ++} ++ ++func TestSourceOperationPostgres(t *testing.T) { ++ for _, profile := range []struct{ name, schema string }{{"default", ""}, {"public", "public"}, {"named", "custodian_named"}} { ++ t.Run(profile.name, func(t *testing.T) { ++ ctx, engine, raw := postgresOperationDB(t, profile.schema) ++ run := func(name string, f func(*testing.T)) { ++ t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) }) ++ } ++ run("create-reconcile-inverse", func(t *testing.T) { ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource()) ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ require.Positive(t, r.SourceID) ++ post := operationRows(t, ctx) ++ again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, r, again) ++ require.Equal(t, post, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ }) ++ run("exact-raw-preexisting-restoration", func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" ++ _, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") ++ require.NoError(t, err) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ run("unchanged-preexisting-survives", func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") ++ require.NoError(t, err) ++ require.Equal(t, "unchanged", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ for _, stage := range []string{"before-apply", "before-inverse"} { ++ run("private-drift-"+stage, func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s) ++ require.NoError(t, err) ++ if stage == "before-inverse" { ++ _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ require.NoError(t, err) ++ } ++ _, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ if stage == "before-apply" { ++ _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ } else { ++ _, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ } ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ } ++ for _, kind := range []string{"user", "external"} { ++ run("linked-"+kind+"-refuses-deletion", func(t *testing.T) { ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") ++ require.NoError(t, err) ++ if kind == "user" { ++ _, err = engine.Insert(&user_model.User{ID: 1, LowerName: "synthetic", Name: "synthetic", Email: "synthetic@invalid.test", LoginSource: r.SourceID}) ++ } else { ++ _, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID) ++ } ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") ++ require.True(t, auth.IsErrSourceInUse(err)) ++ require.Equal(t, before, operationRows(t, ctx)) ++ var count int64 ++ if kind == "user" { ++ count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count() ++ } else { ++ count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count() ++ } ++ require.NoError(t, err) ++ require.EqualValues(t, 1, count) ++ }) ++ } ++ run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) { ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ }) ++ run("driver-error-rollback-and-private-diagnostics", func(t *testing.T) { ++ captured := new(postgresOperationLog) ++ engine.AddHook(&db.SlowQueryHook{Logger: captured}) ++ engine.AddHook(&db.ErrorQueryHook{Logger: captured}) ++ const control = "synthetic-postgres-unmarked-control" ++ _, err := db.GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", control) ++ require.Error(t, err) ++ require.Contains(t, captured.take(), control, "actual unmarked diagnostic control must be observable") ++ _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_fail_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '%', NEW.cfg; END $$`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_fail_source()`) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.PrepareSourceOperation(ctx, "postgres-error-operation", "postgres-error-case", 0, operationSource()) ++ require.NoError(t, err) ++ captured.take() ++ var tracing bytes.Buffer ++ require.NoError(t, trace.Start(&tracing)) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-error-operation", "postgres-error-case") ++ trace.Stop() ++ require.ErrorIs(t, err, auth.ErrSourceOperationStorage) ++ messages := captured.take() ++ require.Contains(t, messages, "[Error SQL Query] [owner-confined query]") ++ require.Contains(t, messages, "[Slow SQL Query] [owner-confined query]") ++ require.NotContains(t, messages, "synthetic-private-original") ++ require.False(t, bytes.Contains(tracing.Bytes(), []byte("synthetic-private-original")), "actual PostgreSQL trace disclosed private cfg") ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-error-operation", "postgres-error-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ }) ++ run("uncooperative-writers-excluded", func(t *testing.T) { ++ locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1) ++ var once sync.Once ++ releaseLock := func() { once.Do(func() { close(release) }) } ++ defer releaseLock() ++ go func() { ++ done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil }) ++ }() ++ select { ++ case <-locked: ++ case err := <-done: ++ t.Fatalf("lock acquisition refused: %v", err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("lock acquisition timed out") ++ } ++ statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`} ++ for _, statement := range statements { ++ session := raw.NewSession() ++ require.NoError(t, session.Begin()) ++ _, err := session.Exec(`SET LOCAL lock_timeout='150ms'`) ++ require.NoError(t, err) ++ _, err = session.Exec(statement) ++ var pgerr *pq.Error ++ blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03" ++ _ = session.Rollback() ++ _ = session.Close() ++ require.True(t, blocked, "an uncooperative writer escaped the actual table lock") ++ } ++ releaseLock() ++ select { ++ case err := <-done: ++ require.NoError(t, err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("transaction did not finish") ++ } ++ for _, statement := range statements { ++ _, err := raw.Exec(statement) ++ require.NoError(t, err) ++ } ++ }) ++ run("concurrent-preparation-has-one-owner", func(t *testing.T) { ++ start, done := make(chan struct{}), make(chan error, 2) ++ for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} { ++ go func(id string) { ++ <-start ++ _, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource()) ++ done <- err ++ }(id) ++ } ++ close(start) ++ ok, conflict := 0, 0 ++ for range 2 { ++ select { ++ case err := <-done: ++ if err == nil { ++ ok++ ++ } else if errors.Is(err, auth.ErrSourceOperationConflict) { ++ conflict++ ++ } else { ++ t.Fatal("unexpected concurrent preparation error") ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("concurrent preparation timed out") ++ } ++ } ++ require.Equal(t, 1, ok) ++ require.Equal(t, 1, conflict) ++ }) ++ }) ++ } ++} ++ ++func TestSourceOperationPostgresProcessRecovery(t *testing.T) { ++ step := os.Getenv("CUSTODIAN_PG_STEP") ++ require.NotEmpty(t, step, "explicit synthetic recovery phase required") ++ ctx, engine, _ := postgresOperationDB(t, "custodian_recovery") ++ switch step { ++ case "prepare": ++ resetPostgresOperations(t, engine) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource()) ++ require.NoError(t, err) ++ case "crash": ++ engine.AddHook(operationCrashAfterSourceInsert{}) ++ _, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ t.Fatal("actual source insert did not reach the process-death hook") ++ case "recover-prepared": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ case "recover-applied": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.EqualValues(t, 1, n) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ case "recover-inverse": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ default: ++ t.Fatal("unknown synthetic recovery phase") ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_process_test.go +@@ -0,0 +1,97 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth_test ++ ++import ( ++ "context" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "strings" ++ "testing" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/stretchr/testify/require" ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++ "xorm.io/xorm/names" ++) ++ ++type operationCrashAfterSourceInsert struct{} ++ ++func (operationCrashAfterSourceInsert) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { ++ return c.Ctx, nil ++} ++func (operationCrashAfterSourceInsert) AfterProcess(c *contexts.ContextHook) error { ++ if c.Err == nil && strings.Contains(c.SQL, "INSERT INTO") && strings.Contains(c.SQL, "login_source") && !strings.Contains(c.SQL, "login_source_operation") { ++ // Process death after the real INSERT, before actual ApplySourceOperation's ++ // post-read/journal update/commit. No artificial operation is substituted. ++ os.Exit(73) ++ } ++ return nil ++} ++func TestSourceOperationProcessRecovery(t *testing.T) { ++ if step := os.Getenv("SOURCE_OPERATION_CHILD_STEP"); step != "" { ++ e, err := xorm.NewEngine("sqlite3", os.Getenv("SOURCE_OPERATION_CHILD_DB")+"?_txlock=deferred") ++ require.NoError(t, err) ++ e.SetMapper(names.GonicMapper{}) ++ db.SetDefaultEngine(context.Background(), e) ++ require.NoError(t, db.SyncAllTables()) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ ctx := db.DefaultContext ++ switch step { ++ case "prepare": ++ _, err = auth.PrepareSourceOperation(ctx, "operation-process-recovery", "case-process-recovery", 0, operationSource()) ++ require.NoError(t, err) ++ case "crash": ++ e.AddHook(operationCrashAfterSourceInsert{}) ++ _, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ t.Fatal("real insert did not reach crash hook") ++ case "recover-prepared": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ n, err := db.GetEngine(ctx).Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ _, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ case "recover-applied": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ case "recover-inverse": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ n, err := db.GetEngine(ctx).Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ default: ++ t.Fatal("unknown synthetic child step") ++ } ++ require.NoError(t, e.Close()) ++ return ++ } ++ exe, err := os.Executable() ++ require.NoError(t, err) ++ path := filepath.Join(t.TempDir(), "owner-private.sqlite") ++ for _, step := range []string{"prepare", "crash", "recover-prepared", "recover-applied", "recover-inverse"} { ++ child := exec.Command(exe, "-test.run=^TestSourceOperationProcessRecovery$", "-test.v") ++ child.Env = append(os.Environ(), "SOURCE_OPERATION_CHILD_STEP="+step, "SOURCE_OPERATION_CHILD_DB="+path) ++ output, err := child.CombinedOutput() ++ if step == "crash" { ++ exit, ok := err.(*exec.ExitError) ++ require.True(t, ok, string(output)) ++ require.Equal(t, 73, exit.ExitCode(), string(output)) ++ } else { ++ require.NoError(t, err, string(output)) ++ } ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_test.go +@@ -0,0 +1,235 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth_test ++ ++import ( ++ "context" ++ "encoding/json" ++ "fmt" ++ "testing" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ "forgejo.org/models/unittest" ++ "forgejo.org/modules/setting" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/stretchr/testify/require" ++) ++ ++func operationDB(t *testing.T) context.Context { ++ t.Helper() ++ require.NoError(t, unittest.PrepareTestDatabase()) ++ _, err := db.GetEngine(db.DefaultContext).Exec("DELETE FROM login_source_operation") ++ require.NoError(t, err) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ return db.DefaultContext ++} ++func operationSource() *auth.Source { ++ return &auth.Source{Type: auth.OAuth2, Name: "operation-test", IsActive: false, Cfg: &oauth2.Source{Provider: "openidConnect", ClientID: "synthetic-client", ClientSecret: "synthetic-private-original", RequiredClaimName: "groups", RequiredClaimValues: []string{"g1", "g2", "g3"}, Scopes: []string{"openid", "profile", "groups"}}} ++} ++func operationRows(t *testing.T, ctx context.Context) string { ++ t.Helper() ++ rows, err := db.GetEngine(ctx).Query("SELECT * FROM login_source ORDER BY id") ++ require.NoError(t, err) ++ b, err := json.Marshal(rows) ++ require.NoError(t, err) ++ return string(b) ++} ++func TestSourceOperationCreateAndInverse(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ r, err := auth.PrepareSourceOperation(ctx, "operation-create", "case-create", 0, operationSource()) ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ require.Positive(t, r.SourceID) ++ id := r.SourceID ++ post := operationRows(t, ctx) ++ r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, id, r.SourceID) ++ require.Equal(t, post, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "operation-create", "wrong-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++} ++func TestSourceOperationRestoresExactRawPreexisting(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ // The old/unknown field must survive restoration byte for byte, including the ++ // private synthetic secret. A typed OAuth2 decoder would silently lose it. ++ raw := `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}` ++ _, err := db.GetEngine(ctx).Exec("UPDATE login_source SET cfg = ? WHERE id = ?", raw, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" ++ _, err = auth.PrepareSourceOperation(ctx, "operation-restore", "case-restore", s.ID, s) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "operation-restore", "case-restore") ++ require.NoError(t, err) ++ _, err = auth.InverseSourceOperation(ctx, "operation-restore", "case-restore") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ source, err := auth.GetSourceByID(ctx, s.ID) ++ require.NoError(t, err) ++ require.Equal(t, s.ID, source.ID) ++} ++func TestSourceOperationUnchangedNeverDeletesPreexisting(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-unchanged", "case-unchanged", s.ID, s) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-unchanged", "case-unchanged") ++ require.NoError(t, err) ++ require.Equal(t, "unchanged", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "operation-unchanged", "case-unchanged") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++} ++func TestSourceOperationPrivateDriftRefuses(t *testing.T) { ++ for _, stage := range []string{"before-apply", "before-inverse"} { ++ t.Run(stage, func(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-private-drift", "case-private-drift", s.ID, s) ++ require.NoError(t, err) ++ if stage == "before-inverse" { ++ _, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ require.NoError(t, err) ++ } ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-drift" ++ require.NoError(t, auth.UpdateSource(ctx, s)) ++ drift := operationRows(t, ctx) ++ if stage == "before-apply" { ++ _, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ } else { ++ _, err = auth.InverseSourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ } ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.NotContains(t, err.Error(), "synthetic-private") ++ require.Equal(t, drift, operationRows(t, ctx)) ++ }) ++ } ++} ++func TestSourceOperationStockCRUDDriftRefuses(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-other-writer", "case-other-writer", 0, operationSource()) ++ require.NoError(t, err) ++ other := operationSource() ++ other.Name = "unrelated-source" ++ require.NoError(t, auth.CreateSource(ctx, other)) ++ drift := operationRows(t, ctx) ++ _, err = auth.ApplySourceOperation(ctx, "operation-other-writer", "case-other-writer") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, drift, operationRows(t, ctx)) ++} ++func TestSourceOperationLinkedSourceRefusesDeletion(t *testing.T) { ++ for _, kind := range []string{"user", "external"} { ++ t.Run(kind, func(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-linked", "case-linked", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-linked", "case-linked") ++ require.NoError(t, err) ++ if kind == "user" { ++ _, err = db.GetEngine(ctx).Exec("UPDATE `user` SET login_source = ? WHERE id = 1", r.SourceID) ++ } else { ++ _, err = db.GetEngine(ctx).Exec("INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES (?, ?, ?)", "synthetic-external", 1, r.SourceID) ++ } ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.InverseSourceOperation(ctx, "operation-linked", "case-linked") ++ require.True(t, auth.IsErrSourceInUse(err)) ++ require.Equal(t, before, operationRows(t, ctx)) ++ if kind == "user" { ++ rows, e := db.GetEngine(ctx).Query("SELECT login_source FROM `user` WHERE id = 1") ++ require.NoError(t, e) ++ require.NotEqual(t, []byte("0"), rows[0]["login_source"]) ++ } else { ++ n, e := db.GetEngine(ctx).Table("external_login_user").Count() ++ require.NoError(t, e) ++ require.EqualValues(t, 1, n) ++ } ++ }) ++ } ++} ++func TestSourceOperationPreparedCancellationAndIDReuse(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err := auth.InverseSourceOperation(ctx, "operation-cancelled", "case-cancelled") ++ require.NoError(t, err) ++ require.Equal(t, "cancelled", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++} ++func TestSourceOperationRejectsUnsafeRuntime(t *testing.T) { ++ ctx := operationDB(t) ++ setting.Database.LogSQL = true ++ t.Cleanup(func() { setting.Database.LogSQL = false }) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-no-logging", "case-no-logging", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported) ++ setting.Database.LogSQL = false ++ err = db.WithTx(ctx, func(tx context.Context) error { ++ _, err := auth.PrepareSourceOperation(tx, "operation-nested", "case-nested", 0, operationSource()) ++ return err ++ }) ++ require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported) ++} ++func TestSourceOperationRefusesSharedManagedSource(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-first-case", "case-first-case", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-first-case", "case-first-case") ++ require.NoError(t, err) ++ shared := operationSource() ++ shared.ID = r.SourceID ++ _, err = auth.PrepareSourceOperation(ctx, "operation-second-case", "case-second-case", r.SourceID, shared) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++} ++func TestSourceOperationBoundsPrivateJournalCustody(t *testing.T) { ++ ctx := operationDB(t) ++ // A full unresolved-journal budget refuses new preparation, without deleting ++ // any intent or replacing private recovery data with an expiry assumption. ++ for i := 0; i < 64; i++ { ++ _, err := db.GetEngine(ctx).Exec("INSERT INTO login_source_operation (operation_id, case_id, target_name, phase) VALUES (?, ?, ?, ?)", fmt.Sprintf("bounded-operation-%03d", i), "bounded-case", fmt.Sprintf("bounded-target-%03d", i), "prepared") ++ require.NoError(t, err) ++ } ++ _, err := auth.PrepareSourceOperation(ctx, "operation-budget-full", "case-budget-full", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ n, err := db.GetEngine(ctx).Table("login_source_operation").Count() ++ require.NoError(t, err) ++ require.EqualValues(t, 64, n) ++} ++func TestSourceOperationRequiresExactPostState(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-post-exact", "case-post-exact", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = db.GetEngine(ctx).Exec("CREATE TRIGGER source_operation_test_drift AFTER INSERT ON login_source BEGIN UPDATE login_source SET name = 'unexpected-rewrite' WHERE id = NEW.id; END") ++ require.NoError(t, err) ++ defer db.GetEngine(ctx).Exec("DROP TRIGGER source_operation_test_drift") ++ _, err = auth.ApplySourceOperation(ctx, "operation-post-exact", "case-post-exact") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-post-exact", "case-post-exact") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++} +--- a/models/db/context.go ++++ b/models/db/context.go +@@ -72,6 +72,12 @@ + // GetEngine will get a db Engine from this context or return an Engine restricted to this context + func GetEngine(ctx context.Context) Engine { + if e := getEngine(ctx); e != nil { ++ // A private-query marker must reach XORM's real query hooks, including ++ // inside an existing engine/transaction context. Wrapping the Go ++ // context alone does not change XORM's stored session context. ++ if isSensitiveSQL(ctx) { ++ return e.Context(ctx) ++ } + return e + } + return x.Context(ctx) +--- a/models/db/engine.go ++++ b/models/db/engine.go +@@ -330,8 +330,15 @@ + func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + ctx, task := trace.NewTask(c.Ctx, "sql") + ctx = context.WithValue(ctx, sqlTask{}, task) +- trace.Log(ctx, "query", c.SQL) +- trace.Logf(ctx, "args", "%v", c.Args) ++ // XORM passes the same ContextHook to every BeforeProcess hook. Publish ++ // the task there too, so a later hook returning c.Ctx retains it. ++ c.Ctx = ctx ++ if isSensitiveSQL(c.Ctx) { ++ trace.Log(ctx, "query", "[owner-confined query]") ++ } else { ++ trace.Log(ctx, "query", c.SQL) ++ trace.Logf(ctx, "args", "%v", c.Args) ++ } + return ctx, nil + } + +@@ -353,7 +360,11 @@ + + func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.ExecuteTime >= h.Treshold { +- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) ++ if isSensitiveSQL(c.Ctx) { ++ h.Logger.Log(8, log.WARN, "[Slow SQL Query] [owner-confined query] - %v", c.ExecuteTime) ++ } else { ++ h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) ++ } + } + return nil + } +@@ -370,7 +381,11 @@ + + func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.Err != nil && !errors.Is(c.Err, context.Canceled) { +- h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) +- } +- return nil +-} ++ if isSensitiveSQL(c.Ctx) { ++ h.Logger.Log(8, log.ERROR, "[Error SQL Query] [owner-confined query]") ++ } else { ++ h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) ++ } ++ } ++ return nil ++} +--- /dev/null ++++ b/models/db/sensitive_sql.go +@@ -0,0 +1,47 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package db ++ ++import ( ++ "context" ++ "forgejo.org/modules/setting" ++ "xorm.io/xorm" ++) ++ ++type sensitiveSQLKey struct{} ++ ++// WithSensitiveSQL suppresses argument, statement and driver-error disclosure ++// in the application's query hooks. The caller must separately refuse raw XORM ++// SQL logging; custom driver/server auditing remains an owner custody concern. ++func WithSensitiveSQL(ctx context.Context) context.Context { ++ return context.WithValue(ctx, sensitiveSQLKey{}, true) ++} ++func isSensitiveSQL(ctx context.Context) bool { ++ sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool) ++ return sensitive ++} ++ ++// SQLLoggingEnabled checks the underlying engine without changing the context or ++// assuming that a sensitive context still exposes an Engine rather than Session. ++// Unknown/uninitialized backends refuse, and query redaction never permits raw ++// XORM logging. It neither creates a transaction nor closes an existing one. ++func SQLLoggingEnabled(ctx context.Context) bool { ++ if setting.Database.LogSQL { ++ return true ++ } ++ engine := getEngine(ctx) ++ if engine == nil { ++ if x == nil { ++ return true ++ } ++ engine = x ++ } ++ switch current := engine.(type) { ++ case *xorm.Engine: ++ return current == nil || current.Logger() == nil || current.Logger().IsShowSQL() ++ case *xorm.Session: ++ return current == nil || current.Engine() == nil || current.Engine().Logger() == nil || current.Engine().Logger().IsShowSQL() ++ default: ++ return true ++ } ++} +--- /dev/null ++++ b/models/db/sensitive_sql_logging_test.go +@@ -0,0 +1,41 @@ ++package db ++ ++import ( ++ "context" ++ "testing" ++ ++ "forgejo.org/modules/setting" ++ "xorm.io/xorm" ++) ++ ++func TestSensitiveSQLLoggingChecksUnderlyingEngine(t *testing.T) { ++ engine, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer engine.Close() ++ engine.SetLogger(NewXORMLogger(false)) ++ session := engine.NewSession() ++ defer session.Close() ++ previous := setting.Database.LogSQL ++ defer func() { setting.Database.LogSQL = previous }() ++ for name, backend := range map[string]Engine{"engine": engine, "session": session} { ++ t.Run(name, func(t *testing.T) { ++ ctx := WithSensitiveSQL(newContext(context.Background(), backend, false)) ++ setting.Database.LogSQL = false ++ engine.ShowSQL(false) ++ if SQLLoggingEnabled(ctx) { ++ t.Fatal("private context with disabled actual SQL logger refused") ++ } ++ engine.ShowSQL(true) ++ if !SQLLoggingEnabled(ctx) { ++ t.Fatal("actual raw SQL logger escaped refusal") ++ } ++ engine.ShowSQL(false) ++ setting.Database.LogSQL = true ++ if !SQLLoggingEnabled(ctx) { ++ t.Fatal("configured raw SQL logger escaped refusal") ++ } ++ }) ++ } ++} +--- /dev/null ++++ b/models/db/sensitive_sql_test.go +@@ -0,0 +1,103 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package db ++ ++import ( ++ "bytes" ++ "context" ++ "errors" ++ "fmt" ++ "runtime/trace" ++ "strings" ++ "testing" ++ "time" ++ ++ "forgejo.org/modules/log" ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++) ++ ++type sensitiveSQLLog struct { ++ log.Logger ++ entries []string ++} ++ ++func (l *sensitiveSQLLog) Log(_ int, _ log.Level, f string, v ...any) { ++ l.entries = append(l.entries, fmt.Sprintf(f, v...)) ++} ++func TestSensitiveSQLHooks(t *testing.T) { ++ const secret = "synthetic-query-private-sentinel" ++ for _, sensitive := range []bool{false, true} { ++ ctx := context.Background() ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ h := &contexts.ContextHook{Ctx: ctx, SQL: "SELECT '" + secret + "'", Args: []any{secret}, Err: errors.New(secret), ExecuteTime: time.Second} ++ logger := new(sensitiveSQLLog) ++ if err := (&SlowQueryHook{Logger: logger}).AfterProcess(h); err != nil { ++ t.Fatal(err) ++ } ++ if err := (&ErrorQueryHook{Logger: logger}).AfterProcess(h); err != nil { ++ t.Fatal(err) ++ } ++ found := strings.Contains(strings.Join(logger.entries, "\n"), secret) ++ if found == sensitive { ++ t.Fatal("query hook disclosure/redaction mismatch") ++ } ++ var out bytes.Buffer ++ if err := trace.Start(&out); err != nil { ++ t.Fatal(err) ++ } ++ next, err := (TracingHook{}).BeforeProcess(h) ++ if err != nil { ++ trace.Stop() ++ t.Fatal(err) ++ } ++ h.Ctx = next ++ err = (TracingHook{}).AfterProcess(h) ++ trace.Stop() ++ if err != nil { ++ t.Fatal(err) ++ } ++ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive { ++ t.Fatal("trace disclosure/redaction mismatch") ++ } ++ } ++} ++ ++func TestSensitiveSQLActualContextPropagation(t *testing.T) { ++ engine, ok := GetEngine(DefaultContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual fixture engine") ++ } ++ logger := new(sensitiveSQLLog) ++ engine.AddHook(&SlowQueryHook{Logger: logger}) ++ engine.AddHook(&ErrorQueryHook{Logger: logger}) ++ engine.AddHook(TracingHook{}) ++ const secret = "synthetic-actual-xorm-private-sentinel" ++ for _, sensitive := range []bool{false, true} { ++ logger.entries = nil ++ ctx := context.Context(DefaultContext) ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ var out bytes.Buffer ++ if err := trace.Start(&out); err != nil { ++ t.Fatal(err) ++ } ++ _, err := GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", secret) ++ trace.Stop() ++ if err == nil { ++ t.Fatal("expected actual SQLite function refusal") ++ } ++ if len(logger.entries) < 2 { ++ t.Fatal("actual query hooks did not execute") ++ } ++ if strings.Contains(strings.Join(logger.entries, "\n"), secret) == sensitive { ++ t.Fatal("actual XORM query context did not preserve redaction") ++ } ++ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive { ++ t.Fatal("actual XORM trace context did not preserve redaction") ++ } ++ } ++} +--- /dev/null ++++ b/models/db/tracing_context_test.go +@@ -0,0 +1,54 @@ ++package db ++ ++import ( ++ "bytes" ++ "context" ++ "runtime/trace" ++ "testing" ++ ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++) ++ ++type followingQueryHook struct{} ++ ++func (followingQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { ++ return c.Ctx, nil ++} ++func (followingQueryHook) AfterProcess(*contexts.ContextHook) error { return nil } ++ ++func TestTracingContextSurvivesFollowingHooks(t *testing.T) { ++ for _, sensitive := range []bool{false, true} { ++ for _, fail := range []bool{false, true} { ++ engine, err := xorm.NewEngine("sqlite3", ":memory:") ++ if err != nil { ++ t.Fatal(err) ++ } ++ engine.AddHook(TracingHook{}) ++ engine.AddHook(followingQueryHook{}) ++ ctx := context.Background() ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ var output bytes.Buffer ++ if err := trace.Start(&output); err != nil { ++ t.Fatal(err) ++ } ++ func() { ++ defer trace.Stop() ++ query := "SELECT ?" ++ if fail { ++ query = "SELECT missing_synthetic_function(?)" ++ } ++ _, err = engine.Context(ctx).Exec(query, "synthetic-following-hook-sentinel") ++ }() ++ _ = engine.Close() ++ if (err != nil) != fail { ++ t.Fatal("unexpected real SQLite query outcome") ++ } ++ if bytes.Contains(output.Bytes(), []byte("synthetic-following-hook-sentinel")) == sensitive { ++ t.Fatal("following hook changed private or ordinary trace disclosure") ++ } ++ } ++ } ++} +--- a/routers/web/admin/auths.go ++++ b/routers/web/admin/auths.go +@@ -322,6 +322,13 @@ + ctx.Data["Source"] = source + ctx.Data["HasTLS"] = source.HasTLS() + ++ // The legacy form has no representation for the opt-in current-UserInfo gate. ++ // Refuse rather than silently dropping it during source reconstruction. ++ if current, ok := source.Cfg.(*oauth2.Source); ok && current.RequiredClaimValues != nil { ++ ctx.RenderWithErr("This OAuth source uses required-claim-values; update it through the reviewed CLI.", tplAuthEdit, form) ++ return ++ } ++ + if ctx.HasError() { + ctx.HTML(http.StatusOK, tplAuthEdit) + return +--- a/routers/web/auth/oauth.go ++++ b/routers/web/auth/oauth.go +@@ -1427,7 +1427,13 @@ + return goth.User{}, err + } + +- if oauth2Source.RequiredClaimName != "" { ++ if oauth2Source.RequiredClaimValues != nil { ++ // The exact callback has already authenticated and enforced the current ++ // Access Engine application.access decision. Group policy lives there. ++ if oauth2Source.Provider != "openidConnect" { ++ return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} ++ } ++ } else if oauth2Source.RequiredClaimName != "" { + claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName] + if !has { + return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine.go +@@ -0,0 +1,224 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package oauth2 ++ ++import ( ++ "context" ++ "crypto" ++ "crypto/x509" ++ "encoding/json" ++ "encoding/pem" ++ "errors" ++ "io" ++ "os" ++ "path/filepath" ++ "strings" ++ "time" ++ ++ "github.com/golang-jwt/jwt/v5" ++ "github.com/markbates/goth" ++ "github.com/netkingdom/access-engine/pkg/api" ++ "github.com/netkingdom/access-engine/pkg/decisionpep" ++) ++ ++var errApplicationDecision = errors.New("current authenticated application access refused") ++ ++// AccessEngineAdmission contains owner-admitted public trust, with only a file ++// reference to the private PDP caller token. It is not persisted OAuth source ++// configuration and cannot be replaced by token claims or provider discovery. ++type AccessEngineAdmission struct { ++ SourceName string `json:"source_name"` ++ Issuer string `json:"issuer"` ++ Audience string `json:"audience"` ++ TokenType string `json:"token_type"` ++ Algorithms []string `json:"algorithms"` ++ IdentityKeys map[string]string `json:"identity_keys"` ++ TenantClaim string `json:"tenant_claim"` ++ PrincipalTypeClaim string `json:"principal_type_claim"` ++ AssuranceClaim string `json:"assurance_claim"` ++ TenantSourceClaim string `json:"tenant_source_claim"` ++ MaxAuthenticationAge string `json:"max_authentication_age"` ++ MaxIdentityAge string `json:"max_identity_age"` ++ Decision decisionpep.Admission `json:"decision"` ++} ++ ++// VerifiedApplicationIdentity is derived only by VerifyApplicationIdentity from ++// independently admitted JOSE keys. RawData and UserInfo never supply these ++// fields. This snapshot is an identity fact, never an authorization verdict. ++type VerifiedApplicationIdentity struct { ++ Issuer string ++ Subject string ++ Tenant string ++ PrincipalType api.SubjectType ++ Assurance string ++ TenantSource string ++ AssuranceData map[string]any ++ AuthenticatedAt time.Time ++ NotAfter time.Time ++} ++ ++func loadAccessEngineAdmission(path string) (AccessEngineAdmission, error) { ++ var a AccessEngineAdmission ++ if !filepath.IsAbs(path) { ++ return a, errApplicationDecision ++ } ++ f, e := os.Open(path) ++ if e != nil { ++ return a, errApplicationDecision ++ } ++ defer f.Close() ++ stat, e := f.Stat() ++ if e != nil || !stat.Mode().IsRegular() || stat.Mode().Perm()&0022 != 0 { ++ return a, errApplicationDecision ++ } ++ raw, e := io.ReadAll(io.LimitReader(f, 1<<20+1)) ++ if e != nil || len(raw) > 1<<20 { ++ return a, errApplicationDecision ++ } ++ d := json.NewDecoder(strings.NewReader(string(raw))) ++ d.DisallowUnknownFields() ++ if d.Decode(&a) != nil { ++ return a, errApplicationDecision ++ } ++ if _, e = d.Token(); e != io.EOF { ++ return a, errApplicationDecision ++ } ++ return a, nil ++} ++ ++// VerifyApplicationIdentity validates the ID token independently of Goth's ++// decode/claim checks and of the UserInfo merge. Keys and accepted algorithm, ++// issuer, audience and type are admitted separately; none come from the JWT URL. ++func VerifyApplicationIdentity(raw string, a AccessEngineAdmission, now time.Time) (VerifiedApplicationIdentity, error) { ++ var out VerifiedApplicationIdentity ++ age, e := time.ParseDuration(a.MaxIdentityAge) ++ authAge, authErr := time.ParseDuration(a.MaxAuthenticationAge) ++ if e != nil || authErr != nil || authAge <= 0 || age <= 0 || a.TenantSourceClaim == "" || a.Issuer == "" || a.Audience == "" || a.TokenType == "" || len(a.Algorithms) == 0 || len(a.IdentityKeys) == 0 || a.TenantClaim == "" || a.PrincipalTypeClaim == "" || a.AssuranceClaim == "" { ++ return out, errApplicationDecision ++ } ++ for _, alg := range a.Algorithms { ++ if alg != "RS256" && alg != "RS384" && alg != "RS512" && alg != "ES256" && alg != "ES384" && alg != "ES512" && alg != "EdDSA" { ++ return out, errApplicationDecision ++ } ++ } ++ claims := jwt.MapClaims{} ++ token, e := jwt.ParseWithClaims(raw, claims, func(t *jwt.Token) (any, error) { ++ if typ, ok := t.Header["typ"].(string); !ok || typ != a.TokenType { ++ return nil, errApplicationDecision ++ } ++ kid, ok := t.Header["kid"].(string) ++ if !ok || kid == "" { ++ return nil, errApplicationDecision ++ } ++ text, ok := a.IdentityKeys[kid] ++ if !ok { ++ return nil, errApplicationDecision ++ } ++ block, rest := pem.Decode([]byte(text)) ++ if block == nil || block.Type != "PUBLIC KEY" || len(strings.TrimSpace(string(rest))) != 0 { ++ return nil, errApplicationDecision ++ } ++ key, e := x509.ParsePKIXPublicKey(block.Bytes) ++ if e != nil { ++ return nil, errApplicationDecision ++ } ++ var pub crypto.PublicKey = key ++ return pub, nil ++ }, jwt.WithValidMethods(a.Algorithms), jwt.WithIssuer(a.Issuer), jwt.WithAudience(a.Audience), jwt.WithExpirationRequired(), jwt.WithIssuedAt(), jwt.WithJSONNumber(), jwt.WithTimeFunc(func() time.Time { return now })) ++ if e != nil || token == nil || !token.Valid { ++ return out, errApplicationDecision ++ } ++ subject, e := claims.GetSubject() ++ if e != nil || subject == "" { ++ return out, errApplicationDecision ++ } ++ issued, e := claims.GetIssuedAt() ++ if e != nil || issued == nil || issued.Time.After(now) || now.Sub(issued.Time) > age { ++ return out, errApplicationDecision ++ } ++ end, e := claims.GetExpirationTime() ++ if e != nil || end == nil || !now.Before(end.Time) { ++ return out, errApplicationDecision ++ } ++ aud, e := claims.GetAudience() ++ if e != nil { ++ return out, errApplicationDecision ++ } ++ if authorizedParty, present := claims["azp"]; present { ++ if azp, ok := authorizedParty.(string); !ok || azp != a.Audience { ++ return out, errApplicationDecision ++ } ++ } else if len(aud) > 1 { ++ return out, errApplicationDecision ++ } ++ value := func(name string) (string, bool) { v, ok := claims[name].(string); return v, ok && validClaimLiteral(v) } ++ tenant, ok := value(a.TenantClaim) ++ if !ok { ++ return out, errApplicationDecision ++ } ++ principal, ok := value(a.PrincipalTypeClaim) ++ if !ok { ++ return out, errApplicationDecision ++ } ++ tenantSource, ok := value(a.TenantSourceClaim) ++ if !ok || (tenantSource != "directory" && tenantSource != "registration" && tenantSource != "default") { ++ return out, errApplicationDecision ++ } ++ assurance, assuranceData, authenticatedAt, authenticationEnd, err := verifiedKeyCapeAssurance(claims[a.AssuranceClaim], issued.Time, now, authAge) ++ if err != nil { ++ return out, errApplicationDecision ++ } ++ validUntil := end.Time ++ for _, bound := range []time.Time{issued.Time.Add(age), authenticationEnd} { ++ if bound.Before(validUntil) { ++ validUntil = bound ++ } ++ } ++ if !now.Before(validUntil) { ++ return out, errApplicationDecision ++ } ++ return VerifiedApplicationIdentity{Issuer: a.Issuer, Subject: subject, Tenant: tenant, PrincipalType: api.SubjectType(principal), Assurance: assurance, TenantSource: tenantSource, AssuranceData: assuranceData, AuthenticatedAt: authenticatedAt, NotAfter: validUntil}, nil ++} ++ ++func (source *Source) authorizeAccessEngine(ctx context.Context, user goth.User) error { ++ // The guarded target extension cannot silently fall back to a local group ++ // decision when admission, keys, facts, PDP or caller authentication is absent. ++ if source.Provider != "openidConnect" || ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues) != nil { ++ return errApplicationDecision ++ } ++ a, e := loadAccessEngineAdmission(os.Getenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE")) ++ if e != nil || source.authSource == nil || a.SourceName != source.authSource.Name || a.Audience != source.ClientID { ++ return errApplicationDecision ++ } ++ identity, e := VerifyApplicationIdentity(user.IDToken, a, time.Now().UTC()) ++ if e != nil { ++ return errApplicationDecision ++ } ++ // RequireCurrentUserInfoClaim has refused missing/malformed current groups ++ // before Goth can merge. The validated ID subject must still match that join. ++ sub, ok := user.RawData["sub"].(string) ++ if !ok || sub != identity.Subject || user.UserID != identity.Subject { ++ return errApplicationDecision ++ } ++ groups, ok := strictClaimValues(user.RawData[source.RequiredClaimName]) ++ if !ok { ++ return errApplicationDecision ++ } ++ client, e := decisionpep.New(a.Decision) ++ if e != nil { ++ return errApplicationDecision ++ } ++ request := api.CheckRequest{Tenant: identity.Tenant, ++ Subject: api.SubjectRef{ID: identity.Subject, Type: identity.PrincipalType, Tenant: identity.Tenant, ++ Attributes: map[string]any{"issuer": identity.Issuer, "assurance": identity.Assurance, "current_userinfo_groups": groups}}, ++ Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: identity.Tenant}, ++ Context: map[string]any{"identity_issuer": identity.Issuer, "identity_subject": identity.Subject, "identity_tenant": identity.Tenant, "identity_principal_type": string(identity.PrincipalType), "identity_assurance": identity.Assurance, "identity_assurance_claim": identity.AssuranceData, "identity_authenticated_at": identity.AuthenticatedAt.UTC().Format(time.RFC3339Nano), "identity_tenant_source": identity.TenantSource, "identity_not_after": identity.NotAfter.UTC().Format(time.RFC3339Nano), "current_userinfo_groups": groups, "required_groups_any_of": source.RequiredClaimValues, "source_name": a.SourceName, "source_id": source.authSource.ID}} ++ if _, e = client.Check(ctx, request); e != nil { ++ return errApplicationDecision ++ } ++ if !time.Now().UTC().Before(identity.NotAfter) { ++ return errApplicationDecision ++ } ++ return nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_assurance.go +@@ -0,0 +1,54 @@ ++package oauth2 ++ ++import ( ++ "encoding/json" ++ "time" ++) ++ ++// Validate the actual IAM v0.3 object only after issuer JOSE verification. These ++// are identity facts, not an access verdict: aal1/service facts still reach the ++// PDP, which owns the application's assurance and principal policy. Preserve the ++// full object and its integer timestamp in the signed decision request. ++func verifiedKeyCapeAssurance(raw any, issued, now time.Time, maxAge time.Duration) (string, map[string]any, time.Time, time.Time, error) { ++ refuse := func() (string, map[string]any, time.Time, time.Time, error) { ++ return "", nil, time.Time{}, time.Time{}, errApplicationDecision ++ } ++ claim, ok := raw.(map[string]any) ++ if !ok || len(claim) != 5 { ++ return refuse() ++ } ++ level, ok := claim["level"].(string) ++ if !ok || (level != "aal1" && level != "aal2" && level != "service") { ++ return refuse() ++ } ++ mfa, ok := claim["mfa"].(bool) ++ if !ok || mfa != (level == "aal2") || claim["source"] != "key-cape" { ++ return refuse() ++ } ++ methods, ok := claim["methods"].([]any) ++ if !ok || len(methods) == 0 { ++ return refuse() ++ } ++ seen := map[string]bool{} ++ for _, method := range methods { ++ value, ok := method.(string) ++ if !ok || !validClaimLiteral(value) || seen[value] { ++ return refuse() ++ } ++ seen[value] = true ++ } ++ number, ok := claim["at"].(json.Number) ++ if !ok { ++ return refuse() ++ } ++ seconds, err := number.Int64() ++ if err != nil || seconds <= 0 || seconds > issued.Unix() || seconds > now.Unix() { ++ return refuse() ++ } ++ authenticated := time.Unix(seconds, 0).UTC() ++ end := authenticated.Add(maxAge) ++ if maxAge <= 0 || !now.Before(end) { ++ return refuse() ++ } ++ return level, claim, authenticated, end, nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_assurance_test.go +@@ -0,0 +1,75 @@ ++package oauth2 ++ ++import ( ++ "encoding/json" ++ "testing" ++ "time" ++) ++ ++func TestAccessEngineKeyCapeAssuranceRefusals(t *testing.T) { ++ cases := map[string]func(map[string]any, time.Time){ ++ "stale-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(-10 * time.Minute).Unix() }, ++ "future-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(time.Minute).Unix() }, ++ "missing-at": func(c map[string]any, _ time.Time) { delete(c, "at") }, ++ "string-at": func(c map[string]any, _ time.Time) { c["at"] = "recent" }, ++ "fractional-at": func(c map[string]any, now time.Time) { c["at"] = float64(now.Unix()) - 1.5 }, ++ "integer-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = 1 }, ++ "inconsistent-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = false }, ++ "wrong-source": func(c map[string]any, _ time.Time) { c["source"] = "userinfo" }, ++ "wrong-level": func(c map[string]any, _ time.Time) { c["level"] = "verified" }, ++ "object-methods": func(c map[string]any, _ time.Time) { c["methods"] = map[string]any{"pwd": true} }, ++ "empty-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{} }, ++ "mixed-methods": func(c map[string]any, _ time.Time) { c["methods"] = []any{"pwd", 1} }, ++ "duplicate-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{"pwd", "pwd"} }, ++ "unknown-object-field": func(c map[string]any, _ time.Time) { c["verified"] = true }, ++ } ++ for name, mutate := range cases { ++ t.Run(name, func(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ mutate(claims["assurance"].(map[string]any), now) ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil { ++ t.Fatal("malformed or stale issuer assurance accepted") ++ } ++ }) ++ } ++ for _, source := range []string{"", "assumed"} { ++ admission, key, claims, now := identityFixture(t) ++ claims["tenant_source"] = source ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil { ++ t.Fatal("missing or unknown tenant provenance accepted") ++ } ++ } ++} ++ ++func TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ admission.MaxAuthenticationAge = "5s" ++ claims["assurance"].(map[string]any)["at"] = now.Add(-3 * time.Second).Unix() ++ identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if !identity.NotAfter.Equal(now.Add(2*time.Second)) || !identity.AuthenticatedAt.Equal(now.Add(-3*time.Second)) || identity.TenantSource != "directory" { ++ t.Fatal("snapshot overstated authentication freshness or tenant provenance") ++ } ++ if _, ok := identity.AssuranceData["at"].(json.Number); !ok || identity.AssuranceData["mfa"] != true || identity.AssuranceData["source"] != "key-cape" { ++ t.Fatal("source assurance object lost its actual wire types") ++ } ++ admission.MaxIdentityAge = "2s" ++ identity, err = VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil || !identity.NotAfter.Equal(now.Add(time.Second)) { ++ t.Fatal("snapshot overstated token-age validity", err) ++ } ++} ++ ++func TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ claim := claims["assurance"].(map[string]any) ++ claim["level"] = "aal1" ++ claim["mfa"] = false ++ claim["methods"] = []string{"pwd"} ++ identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil || identity.Assurance != "aal1" || identity.AssuranceData["mfa"] != false { ++ t.Fatal("identity parser substituted an application authorization policy", err) ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_azp_test.go +@@ -0,0 +1,34 @@ ++package oauth2 ++ ++import "testing" ++ ++func TestAccessEngineAuthorizedPartyRefusesAnotherClient(t *testing.T) { ++ for name, value := range map[string]any{ ++ "other-client": "other-client", ++ "number": 17, ++ "array": []string{"forgejo"}, ++ "empty": "", ++ } { ++ t.Run(name, func(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ claims["azp"] = value ++ token := signedIdentity(t, key, claims, nil) ++ if _, err := VerifyApplicationIdentity(token, admission, now); err == nil { ++ t.Fatal("a valid signature and audience concealed an invalid authorized party") ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineAuthorizedPartyAcceptsAdmittedClient(t *testing.T) { ++ for _, multiple := range []bool{false, true} { ++ admission, key, claims, now := identityFixture(t) ++ claims["azp"] = admission.Audience ++ if multiple { ++ claims["aud"] = []string{admission.Audience, "another-resource"} ++ } ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err != nil { ++ t.Fatal("admitted authorized party refused", err) ++ } ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_callback_test.go +@@ -0,0 +1,194 @@ ++package oauth2 ++ ++import ( ++ "crypto/ed25519" ++ "crypto/rand" ++ "encoding/base64" ++ "encoding/json" ++ "encoding/pem" ++ "net/http" ++ "net/http/httptest" ++ "os" ++ "path/filepath" ++ "reflect" ++ "strings" ++ "sync/atomic" ++ "testing" ++ "time" ++ ++ authmodel "forgejo.org/models/auth" ++ "github.com/golang-jwt/jwt/v5" ++ "github.com/markbates/goth" ++ "github.com/markbates/goth/gothic" ++ "github.com/netkingdom/access-engine/pkg/api" ++ "github.com/netkingdom/access-engine/pkg/decisionpep" ++ "github.com/netkingdom/access-engine/testsupport/signfixture" ++) ++ ++// This fixture substitutes only Goth's completed authentication result. The ++// actual Source.Callback, JOSE verifier, HTTPS PEP client and producer signature ++// implementation execute. Actual issuer login and native admission are separate. ++func callbackFixture(t *testing.T, group, mode string) (*Source, *goth.User, *atomic.Int64) { ++ return callbackFixtureWithIdentity(t, group, mode, nil, "") ++} ++ ++func callbackFixtureWithIdentity(t *testing.T, group, mode string, suppliedAdmission *AccessEngineAdmission, suppliedToken string) (*Source, *goth.User, *atomic.Int64) { ++ t.Helper() ++ admission, identityKey, claims, _ := identityFixture(t) ++ identityToken := signedIdentity(t, identityKey, claims, nil) ++ if suppliedAdmission != nil { ++ admission, identityToken = *suppliedAdmission, suppliedToken ++ } ++ verified, err := VerifyApplicationIdentity(identityToken, admission, time.Now().UTC()) ++ if err != nil { ++ t.Fatal("callback fixture identity invalid") ++ } ++ pub, key, err := ed25519.GenerateKey(rand.Reader) ++ if err != nil { ++ t.Fatal(err) ++ } ++ calls := new(atomic.Int64) ++ server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ calls.Add(1) ++ if r.URL.Path != "/v1/check" || r.Method != http.MethodPost || r.Header.Get("Authorization") != "Bearer synthetic-caller" || r.Header.Get("Access-Engine-JSON-Profile") != decisionpep.JSONProfile { ++ t.Error("incorrect authenticated PDP request") ++ w.WriteHeader(403) ++ return ++ } ++ var request api.CheckRequest ++ decoder := json.NewDecoder(r.Body) ++ decoder.UseNumber() ++ if err := decoder.Decode(&request); err != nil { ++ t.Error(err) ++ w.WriteHeader(400) ++ return ++ } ++ c := request.Context ++ if request.Subject.ID != "user:synthetic" || request.Subject.Tenant != "tenant:synthetic" || request.Subject.Type != "human" || request.Action != "application.access" || request.Resource.ID != "application:forgejo" || c["identity_issuer"] != admission.Issuer || c["identity_assurance"] != "aal2" || c["identity_tenant"] != "tenant:synthetic" || c["source_id"] != json.Number("17") || !reflect.DeepEqual(c["identity_assurance_claim"], verified.AssuranceData) || c["identity_authenticated_at"] != verified.AuthenticatedAt.UTC().Format(time.RFC3339Nano) || c["identity_tenant_source"] != verified.TenantSource { ++ t.Error("PDP request did not use authenticated identity and source") ++ w.WriteHeader(400) ++ return ++ } ++ now := time.Now().UTC() ++ envelope := api.DecisionEnvelope{ID: "synthetic-decision", RequestID: request.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: "v1", Subject: request.Subject, Resource: request.Resource, Binding: api.NewDecisionBindingFor(request, request), Provenance: api.DecisionProvenance{Evaluator: "flex-auth/local", Mode: "standalone/" + decisionpep.JSONProfile, PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyPackageDigest: "sha256:" + strings.Repeat("1", 64), RegistrySnapshotDigest: "sha256:" + strings.Repeat("2", 64), Caller: &api.CallerProvenance{Mode: "enforce", Principal: "system:serviceaccount:synthetic:forgejo", Audience: "access-engine", NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}} ++ api.CompleteDecision(&envelope, request, api.DecisionCompletion{AllowTTL: "10s", Now: now}) ++ switch mode { ++ case "deny": ++ envelope.Effect = api.DecisionEffectDeny ++ case "wrong-binding": ++ envelope.Binding.Subject.ID = "other" ++ case "wrong-pin": ++ envelope.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64) ++ case "expired": ++ envelope.Lifetime.ExpiresAt = now.Add(-time.Hour).Format(time.RFC3339Nano) ++ } ++ if mode != "unsigned" { ++ signingKey := key ++ if mode == "wrong-key" { ++ _, signingKey, err = ed25519.GenerateKey(rand.Reader) ++ if err != nil { ++ t.Error(err) ++ return ++ } ++ } ++ if err := signfixture.Sign(&envelope, signingKey, "synthetic-pdp"); err != nil { ++ t.Error(err) ++ return ++ } ++ } ++ w.Header().Set("Content-Type", "application/json") ++ if err := json.NewEncoder(w).Encode(envelope); err != nil { ++ t.Error(err) ++ } ++ })) ++ t.Cleanup(server.Close) ++ dir := t.TempDir() ++ ca := filepath.Join(dir, "ca.pem") ++ token := filepath.Join(dir, "caller") ++ config := filepath.Join(dir, "admission.json") ++ if err := os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0600); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(token, []byte("synthetic-caller"), 0600); err != nil { ++ t.Fatal(err) ++ } ++ admission.SourceName = "synthetic-source" ++ admission.Decision = decisionpep.Admission{Endpoint: server.URL + "/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"synthetic-pdp": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:synthetic:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "2s"} ++ raw, err := json.Marshal(admission) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if err = os.WriteFile(config, raw, 0600); err != nil { ++ t.Fatal(err) ++ } ++ t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", config) ++ source := &Source{Provider: "openidConnect", ClientID: admission.Audience, RequiredClaimName: "groups", RequiredClaimValues: []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}} ++ source.authSource = &authmodel.Source{ID: 17, Name: admission.SourceName} ++ user := &goth.User{UserID: "user:synthetic", IDToken: identityToken, RawData: map[string]any{"sub": "user:synthetic", "groups": []string{group}}} ++ previous := gothic.CompleteUserAuth ++ gothic.CompleteUserAuth = func(http.ResponseWriter, *http.Request) (goth.User, error) { return *user, nil } ++ t.Cleanup(func() { gothic.CompleteUserAuth = previous }) ++ return source, user, calls ++} ++ ++func TestAccessEngineActualCallbackAllowsAuthenticatedDecision(t *testing.T) { ++ for _, group := range []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} { ++ t.Run(group, func(t *testing.T) { ++ source, user, calls := callbackFixture(t, group, "allow") ++ // Mutable UserInfo cannot supply identity assurance, issuer or tenant. ++ user.RawData["acr"] = "aal1" ++ user.RawData["assurance"] = map[string]any{"level": "aal1", "source": "attacker"} ++ user.RawData["tenant_source"] = "attacker" ++ user.RawData["tenant"] = "tenant:attacker" ++ user.RawData["iss"] = "https://attacker.invalid" ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err != nil || got.UserID != user.UserID || calls.Load() != 1 { ++ t.Fatalf("callback result: err=%v requests=%d", err, calls.Load()) ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineActualCallbackRefusesUnverifiedPDP(t *testing.T) { ++ for _, mode := range []string{"deny", "unsigned", "wrong-key", "wrong-pin", "wrong-binding", "expired"} { ++ t.Run(mode, func(t *testing.T) { ++ source, _, calls := callbackFixture(t, "forge-admins", mode) ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err == nil || got.UserID != "" || got.IDToken != "" || calls.Load() != 1 { ++ t.Fatalf("unverified decision escaped callback: requests=%d err=%v", calls.Load(), err) ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineActualCallbackChecksIdentityBeforePDP(t *testing.T) { ++ cases := map[string]func(*Source, *goth.User){ ++ "missing-token": func(_ *Source, u *goth.User) { u.IDToken = "" }, ++ "mismatched-user": func(_ *Source, u *goth.User) { u.UserID = "other" }, ++ "mismatched-subject": func(_ *Source, u *goth.User) { u.RawData["sub"] = "other" }, ++ "missing-groups": func(_ *Source, u *goth.User) { delete(u.RawData, "groups") }, ++ "wrong-source": func(s *Source, _ *goth.User) { s.authSource.Name = "other" }, ++ "wrong-provider": func(s *Source, _ *goth.User) { s.Provider = "other" }, ++ "wrong-audience": func(s *Source, _ *goth.User) { s.ClientID = "other" }, ++ "missing-admission": func(_ *Source, _ *goth.User) { t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", "") }, ++ "expired-token": func(_ *Source, u *goth.User) { ++ _, key, claims, now := identityFixture(t) ++ claims["exp"] = now.Add(-time.Hour).Unix() ++ u.IDToken = signedIdentity(t, key, claims, nil) ++ }, ++ "untrusted-identity": func(_ *Source, u *goth.User) { ++ token := jwt.NewWithClaims(jwt.SigningMethodNone, jwt.MapClaims{"sub": "user:synthetic"}) ++ u.IDToken, _ = token.SignedString(jwt.UnsafeAllowNoneSignatureType) ++ }, ++ } ++ for name, mutate := range cases { ++ t.Run(name, func(t *testing.T) { ++ source, user, calls := callbackFixture(t, "forge-admins", "allow") ++ mutate(source, user) ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err == nil || got.UserID != "" || calls.Load() != 0 { ++ t.Fatalf("unverified identity reached PDP: requests=%d err=%v", calls.Load(), err) ++ } ++ }) ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_test.go +@@ -0,0 +1,100 @@ ++package oauth2 ++ ++import ( ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/x509" ++ "encoding/pem" ++ "testing" ++ "time" ++ ++ "github.com/golang-jwt/jwt/v5" ++) ++ ++func identityFixture(t *testing.T) (AccessEngineAdmission, ed25519.PrivateKey, jwt.MapClaims, time.Time) { ++ t.Helper() ++ pub, key, e := ed25519.GenerateKey(rand.Reader) ++ if e != nil { ++ t.Fatal(e) ++ } ++ der, e := x509.MarshalPKIXPublicKey(pub) ++ if e != nil { ++ t.Fatal(e) ++ } ++ now := time.Now().UTC().Truncate(time.Second) ++ a := AccessEngineAdmission{Issuer: "https://issuer.invalid", Audience: "forgejo", TokenType: "JWT", Algorithms: []string{"EdDSA"}, IdentityKeys: map[string]string{"synthetic": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))}, TenantClaim: "tenant", PrincipalTypeClaim: "principal_type", AssuranceClaim: "assurance", TenantSourceClaim: "tenant_source", MaxAuthenticationAge: "5m", MaxIdentityAge: "5m"} ++ c := jwt.MapClaims{"iss": a.Issuer, "aud": a.Audience, "sub": "user:synthetic", "iat": now.Add(-time.Second).Unix(), "exp": now.Add(time.Minute).Unix(), "tenant": "tenant:synthetic", "principal_type": "human", "tenant_source": "directory", "assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []string{"pwd", "otp"}, "at": now.Add(-time.Second).Unix()}} ++ return a, key, c, now ++} ++func signedIdentity(t *testing.T, key ed25519.PrivateKey, c jwt.MapClaims, mutate func(*jwt.Token)) string { ++ t.Helper() ++ token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ if mutate != nil { ++ mutate(token) ++ } ++ raw, e := token.SignedString(key) ++ if e != nil { ++ t.Fatal(e) ++ } ++ return raw ++} ++func TestVerifiedApplicationIdentityActualJOSE(t *testing.T) { ++ a, key, c, now := identityFixture(t) ++ raw := signedIdentity(t, key, c, nil) ++ got, e := VerifyApplicationIdentity(raw, a, now) ++ if e != nil { ++ t.Fatal(e) ++ } ++ if got.Subject != "user:synthetic" || got.Tenant != "tenant:synthetic" || got.Assurance != "aal2" || got.Issuer != a.Issuer || string(got.PrincipalType) != "human" { ++ t.Fatal("identity did not come from verified token") ++ } ++ cases := map[string]func(jwt.MapClaims){"issuer": func(c jwt.MapClaims) { c["iss"] = "https://other.invalid" }, "audience": func(c jwt.MapClaims) { c["aud"] = "other" }, "expired": func(c jwt.MapClaims) { c["exp"] = now.Add(-time.Nanosecond).Unix() }, "future-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(time.Minute).Unix() }, "old-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(-10 * time.Minute).Unix() }, "no-exp": func(c jwt.MapClaims) { delete(c, "exp") }, "no-iat": func(c jwt.MapClaims) { delete(c, "iat") }, "no-tenant": func(c jwt.MapClaims) { delete(c, "tenant") }, "no-principal": func(c jwt.MapClaims) { delete(c, "principal_type") }, "no-assurance": func(c jwt.MapClaims) { delete(c, "assurance") }, "multi-audience-no-azp": func(c jwt.MapClaims) { c["aud"] = []string{"forgejo", "other"} }, "empty-subject": func(c jwt.MapClaims) { c["sub"] = "" }} ++ for name, change := range cases { ++ t.Run(name, func(t *testing.T) { ++ copy := jwt.MapClaims{} ++ for k, v := range c { ++ copy[k] = v ++ } ++ change(copy) ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, copy, nil), a, now); e == nil { ++ t.Fatal("unverified identity accepted") ++ } ++ }) ++ } ++ t.Run("wrong-key", func(t *testing.T) { ++ _, other, _ := ed25519.GenerateKey(rand.Reader) ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, other, c, nil), a, now); e == nil { ++ t.Fatal("wrong key accepted") ++ } ++ }) ++ t.Run("unknown-key", func(t *testing.T) { ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["kid"] = "other" }), a, now); e == nil { ++ t.Fatal("unknown key accepted") ++ } ++ }) ++ t.Run("wrong-type", func(t *testing.T) { ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["typ"] = "at+jwt" }), a, now); e == nil { ++ t.Fatal("access token accepted as ID token") ++ } ++ }) ++ t.Run("algorithm-confusion", func(t *testing.T) { ++ token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ raw, _ := token.SignedString([]byte(a.IdentityKeys["synthetic"])) ++ if _, e := VerifyApplicationIdentity(raw, a, now); e == nil { ++ t.Fatal("algorithm confusion accepted") ++ } ++ }) ++ t.Run("none", func(t *testing.T) { ++ token := jwt.NewWithClaims(jwt.SigningMethodNone, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ raw, _ := token.SignedString(jwt.UnsafeAllowNoneSignatureType) ++ if _, e := VerifyApplicationIdentity(raw, a, now); e == nil { ++ t.Fatal("unsigned identity accepted") ++ } ++ }) ++} +--- a/services/auth/source/oauth2/providers_openid.go ++++ b/services/auth/source/oauth2/providers_openid.go +@@ -4,6 +4,7 @@ + package oauth2 + + import ( ++ "errors" + "html/template" + + "forgejo.org/modules/log" +@@ -43,6 +44,22 @@ + if err != nil { + log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err) + } ++ if err != nil { ++ return nil, err ++ } ++ if source.RequiredClaimValues != nil { ++ if err := ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues); err != nil { ++ return nil, err ++ } ++ if provider.OpenIDConfig == nil || provider.SkipUserInfoRequest { ++ return nil, errors.New("required-claim-values needs current UserInfo") ++ } ++ guarded, err := RequireCurrentUserInfoClaim(provider.Client(), provider.OpenIDConfig.UserInfoEndpoint, source.RequiredClaimName) ++ if err != nil { ++ return nil, err ++ } ++ provider.HTTPClient = guarded ++ } + return provider, err + } + +--- /dev/null ++++ b/services/auth/source/oauth2/required_claim_values.go +@@ -0,0 +1,202 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package oauth2 ++ ++import ( ++ "bytes" ++ "encoding/json" ++ "errors" ++ "io" ++ "net/http" ++ "net/url" ++ "strings" ++ "unicode" ++ "unicode/utf8" ++) ++ ++const maxClaimValues = 1024 ++const maxUserInfoBytes = 1 << 20 ++ ++// ValidateRequiredClaimValues validates only the opt-in list extension. A nil ++// list retains every legacy single-value behavior. An explicitly empty list ++// fails closed rather than turning a configured gate into claim-presence-only. ++func ValidateRequiredClaimValues(name, legacy string, values []string) error { ++ if values == nil { ++ return nil ++ } ++ if !validClaimLiteral(name) || legacy != "" || len(values) == 0 || len(values) > 64 { ++ return errors.New("invalid or conflicting required-claim-values configuration") ++ } ++ seen := map[string]bool{} ++ for _, value := range values { ++ if !validClaimLiteral(value) || seen[value] { ++ return errors.New("invalid required-claim-values literal") ++ } ++ seen[value] = true ++ } ++ return nil ++} ++ ++func validClaimLiteral(value string) bool { ++ return value != "" && len(value) <= 256 && utf8.ValidString(value) && ++ value == strings.TrimSpace(value) && !strings.ContainsAny(value, "\x00\r\n,") && ++ !strings.ContainsFunc(value, func(r rune) bool { return unicode.IsControl(r) || r == utf8.RuneError }) ++} ++ ++// strictClaimValues accepts only a well-formed claim array. No scalar parsing, ++// coercion, regex or prefix matching is introduced into the new gate. ++func strictClaimValues(raw any) ([]string, bool) { ++ var values []string ++ switch v := raw.(type) { ++ case []string: ++ values = v ++ case []any: ++ values = make([]string, len(v)) ++ for i, value := range v { ++ s, ok := value.(string) ++ if !ok { ++ return nil, false ++ } ++ values[i] = s ++ } ++ default: ++ return nil, false ++ } ++ if len(values) > maxClaimValues { ++ return nil, false ++ } ++ for _, value := range values { ++ if !validClaimLiteral(value) { ++ return nil, false ++ } ++ } ++ return values, true ++} ++ ++// MatchRequiredClaimValues evaluates the new consumer gate against current ++// authenticated provider data; it does not mutate or manufacture issuer claims. ++func MatchRequiredClaimValues(claims map[string]any, name, legacy string, required []string) bool { ++ if required == nil || ValidateRequiredClaimValues(name, legacy, required) != nil { ++ return false ++ } ++ raw, present := claims[name] ++ if !present { ++ return false ++ } ++ values, ok := strictClaimValues(raw) ++ if !ok { ++ return false ++ } ++ for _, value := range values { ++ for _, wanted := range required { ++ if value == wanted { ++ return true ++ } ++ } ++ } ++ return false ++} ++ ++// RequireCurrentUserInfoClaim preserves the existing HTTP client settings and ++// adds a strict response guard for this opt-in source only. A current UserInfo ++// array must overwrite any stale ID-token claim. Missing/invalid data and HTTP ++// failures cannot fall back to the ID-token map in Goth's merging code. ++func RequireCurrentUserInfoClaim(client *http.Client, endpoint, claim string) (*http.Client, error) { ++ u, err := url.Parse(endpoint) ++ if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Fragment != "" || !validClaimLiteral(claim) { ++ return nil, errors.New("required-claim-values needs an HTTPS UserInfo endpoint") ++ } ++ if client == nil { ++ client = http.DefaultClient ++ } ++ result := *client ++ base := result.Transport ++ if base == nil { ++ base = http.DefaultTransport ++ } ++ result.Transport = ¤tClaimTransport{base: base, endpoint: endpoint, claim: claim} ++ // Redirects could evade endpoint matching or send a bearer to another origin. ++ // They are unsupported for the new guarded provider; legacy clients are intact. ++ result.CheckRedirect = func(*http.Request, []*http.Request) error { ++ return errors.New("guarded OAuth redirects are not supported") ++ } ++ return &result, nil ++} ++ ++type currentClaimTransport struct { ++ base http.RoundTripper ++ endpoint, claim string ++} ++ ++func (t *currentClaimTransport) RoundTrip(req *http.Request) (*http.Response, error) { ++ response, err := t.base.RoundTrip(req) ++ if req.URL.String() != t.endpoint { ++ return response, err ++ } ++ if err != nil { ++ if response != nil && response.Body != nil { ++ response.Body.Close() ++ } ++ return nil, errors.New("current UserInfo lookup failed") ++ } ++ if response == nil || response.Body == nil { ++ return nil, errors.New("current UserInfo response missing") ++ } ++ defer response.Body.Close() ++ if req.Method != http.MethodGet || response.StatusCode != http.StatusOK { ++ return nil, errors.New("current UserInfo refused") ++ } ++ body, err := io.ReadAll(io.LimitReader(response.Body, maxUserInfoBytes+1)) ++ if err != nil || len(body) > maxUserInfoBytes || !utf8.Valid(body) { ++ return nil, errors.New("current UserInfo response invalid") ++ } ++ // Reject duplicate top-level keys before Unmarshal can hide a conflicting ++ // groups or subject claim. json.RawMessage permits unrelated normal claims. ++ decoder := json.NewDecoder(bytes.NewReader(body)) ++ token, err := decoder.Token() ++ if err != nil || token != json.Delim('{') { ++ return nil, errors.New("current UserInfo object required") ++ } ++ claims := map[string]json.RawMessage{} ++ for decoder.More() { ++ keyToken, err := decoder.Token() ++ if err != nil { ++ return nil, errors.New("current UserInfo key invalid") ++ } ++ key, ok := keyToken.(string) ++ if !ok { ++ return nil, errors.New("current UserInfo key invalid") ++ } ++ if _, duplicate := claims[key]; duplicate { ++ return nil, errors.New("current UserInfo duplicate claim") ++ } ++ var value json.RawMessage ++ if decoder.Decode(&value) != nil { ++ return nil, errors.New("current UserInfo value invalid") ++ } ++ claims[key] = value ++ } ++ if token, err = decoder.Token(); err != nil || token != json.Delim('}') { ++ return nil, errors.New("current UserInfo object invalid") ++ } ++ var trailing any ++ if decoder.Decode(&trailing) != io.EOF { ++ return nil, errors.New("current UserInfo trailing data") ++ } ++ raw, present := claims[t.claim] ++ if !present { ++ return nil, errors.New("current UserInfo required claim missing") ++ } ++ var value any ++ if json.Unmarshal(raw, &value) != nil { ++ return nil, errors.New("current UserInfo required claim invalid") ++ } ++ if _, ok := strictClaimValues(value); !ok { ++ return nil, errors.New("current UserInfo required claim array invalid") ++ } ++ // Return the original public response bytes to Goth. Subject equality and ++ // audience/issuer checks remain in its existing authenticated provider path. ++ response.Body = io.NopCloser(bytes.NewReader(body)) ++ return response, nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/required_claim_values_test.go +@@ -0,0 +1,144 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package oauth2 ++ ++import ( ++ "errors" ++ "io" ++ "net/http" ++ "reflect" ++ "strings" ++ "testing" ++) ++ ++var caseValues = []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} ++ ++type roundTripFunc func(*http.Request) (*http.Response, error) ++ ++func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } ++ ++func mockUserInfo(body string, status int) *http.Client { ++ return &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { ++ return &http.Response{StatusCode: status, Header: http.Header{}, Body: io.NopCloser(strings.NewReader(body)), Request: r}, nil ++ })} ++} ++ ++func TestRequiredClaimExactAnyOfAndNoMutation(t *testing.T) { ++ for _, group := range caseValues { ++ groups := []any{group, "unrelated"} ++ claims := map[string]any{"groups": groups, "other": "preserved"} ++ if !MatchRequiredClaimValues(claims, "groups", "", caseValues) { ++ t.Fatal("individual allowed group rejected") ++ } ++ if !reflect.DeepEqual(claims, map[string]any{"groups": groups, "other": "preserved"}) { ++ t.Fatal("claims mutated") ++ } ++ } ++ if !MatchRequiredClaimValues(map[string]any{"groups": append([]string(nil), caseValues...)}, "groups", "", caseValues) { ++ t.Fatal("multiple allowed groups rejected") ++ } ++ for _, raw := range []any{nil, true, 1, "forge-admins", "forge-admins,forge-coulomb-owners", []any{"forge-admins", 1}, []any{"forge-admins", nil}, []string{}, []any{}, []string{"forge-admins-lookalike"}, []string{" Forge-admins"}, []string{"forge-admins\n"}, []string{"Forge-admins"}, []string{"forge-admins,other"}, []string{"forge-admins", ""}} { ++ if MatchRequiredClaimValues(map[string]any{"groups": raw}, "groups", "", caseValues) { ++ t.Fatal("malformed/nonmatching claim accepted") ++ } ++ } ++ if MatchRequiredClaimValues(map[string]any{}, "groups", "", caseValues) { ++ t.Fatal("absent claim accepted") ++ } ++ if MatchRequiredClaimValues(map[string]any{"unrelated": []string{"forge-admins"}}, "groups", "", caseValues) { ++ t.Fatal("unrelated claim used") ++ } ++} ++ ++func TestRequiredClaimConfigurationRefusalsAndLegacyIsolation(t *testing.T) { ++ if ValidateRequiredClaimValues("", "old arbitrary literal", nil) != nil { ++ t.Fatal("legacy config changed") ++ } ++ for _, cfg := range []struct { ++ name, legacy string ++ values []string ++ }{ ++ {"groups", "", []string{}}, {"groups", "", []string{""}}, {"groups", "old", caseValues}, {"", "", caseValues}, ++ {"groups", "", []string{"a", "a"}}, {"groups", "", []string{"a,b"}}, {"groups", "", []string{" a"}}, ++ } { ++ if ValidateRequiredClaimValues(cfg.name, cfg.legacy, cfg.values) == nil { ++ t.Fatal("invalid config accepted") ++ } ++ } ++ if ValidateRequiredClaimValues("groups", "", caseValues) != nil { ++ t.Fatal("case config invalid") ++ } ++} ++ ++func TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable(t *testing.T) { ++ for _, body := range []string{ ++ `{}`, `{"sub":"alice"}`, `{"groups":null}`, `{"groups":true}`, `{"groups":"forge-admins"}`, ++ `{"groups":["forge-admins",null]}`, `{"groups":["forge-admins",1]}`, ++ `{"groups":["forge-admins"],"groups":[]}`, `{"groups":[]} {}`, `[]`, `{"groups":[]`, ++ `{"groups":["forge-admins",""]}`, strings.Repeat(" ", maxUserInfoBytes+1), ++ } { ++ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := client.Get("https://issuer.example/userinfo") ++ if err == nil { ++ response.Body.Close() ++ t.Fatal("invalid current claim did not fail closed") ++ } ++ } ++ client, _ := RequireCurrentUserInfoClaim(mockUserInfo(`{"groups":[]}`, 503), "https://issuer.example/userinfo", "groups") ++ if response, err := client.Get("https://issuer.example/userinfo"); err == nil { ++ response.Body.Close() ++ t.Fatal("non200 accepted") ++ } ++ broken := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { return nil, errors.New("private backend error") })} ++ client, _ = RequireCurrentUserInfoClaim(broken, "https://issuer.example/userinfo", "groups") ++ if _, err := client.Get("https://issuer.example/userinfo"); err == nil || strings.Contains(err.Error(), "private backend") { ++ t.Fatal("lookup failure not contained") ++ } ++} ++ ++func TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval(t *testing.T) { ++ for _, body := range []string{`{"sub":"alice","groups":[]}`, `{"sub":"alice","groups":["forge-coulomb-readers"]}`} { ++ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := client.Get("https://issuer.example/userinfo") ++ if err != nil { ++ t.Fatal(err) ++ } ++ b, _ := io.ReadAll(response.Body) ++ response.Body.Close() ++ if string(b) != body { ++ t.Fatal("response changed") ++ } ++ } ++} ++ ++func TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse(t *testing.T) { ++ original := mockUserInfo(`{"unrelated":true}`, 200) ++ guarded, err := RequireCurrentUserInfoClaim(original, "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := original.Get("https://issuer.example/userinfo") ++ if err != nil { ++ t.Fatal("original client mutated") ++ } ++ response.Body.Close() ++ response, err = guarded.Get("https://issuer.example/unrelated") ++ if err != nil { ++ t.Fatal("unrelated endpoint changed") ++ } ++ response.Body.Close() ++ if guarded.CheckRedirect == nil || guarded.CheckRedirect(nil, nil) == nil { ++ t.Fatal("redirect can bypass guard") ++ } ++ for _, endpoint := range []string{"", "http://issuer.example/userinfo", "https://user:pass@issuer.example/userinfo", "https://issuer.example/userinfo#fragment"} { ++ if _, err := RequireCurrentUserInfoClaim(original, endpoint, "groups"); err == nil { ++ t.Fatal("invalid endpoint accepted") ++ } ++ } ++} +--- a/services/auth/source/oauth2/source.go ++++ b/services/auth/source/oauth2/source.go +@@ -22,6 +22,7 @@ + Scopes []string + AttributeSSHPublicKey string + RequiredClaimName string ++ RequiredClaimValues []string `json:",omitempty"` + RequiredClaimValue string + GroupClaimName string + AdminGroup string +--- a/services/auth/source/oauth2/source_callout.go ++++ b/services/auth/source/oauth2/source_callout.go +@@ -64,5 +64,11 @@ + return user, err + } + ++ if source.RequiredClaimValues != nil { ++ if err := source.authorizeAccessEngine(request.Context(), user); err != nil { ++ return goth.User{}, err ++ } ++ } ++ + return user, nil + } +--- /dev/null ++++ b/third_party/access-engine-pep/LICENSE +@@ -0,0 +1,151 @@ ++# Target Revenue Source License ++ ++**Version 1.0, Candidate 1 (V1C1)** ++ ++--- ++ ++> **PRELIMINARY CANDIDATE — SUBJECT TO CHANGE — NOT FINAL** ++> ++> This repository is governed by the Target Revenue Source License ++> (TRSL), Version 1, Candidate 1. This is the framework's first working ++> candidate, adopted as the org's preliminary operating license across ++> all repos (maintainer decision, 2026-07-29) during the build/alpha ++> stage — see `SCOPE.md` §1 and Appendix A of the canonical text (link ++> below) for the alpha/beta risk-acceptance decision this reflects and ++> what it does and does not mean. Full specialist legal review is ++> explicitly deferred until the framework moves out of beta. ++> ++> **Canonical source, full candidate-status banner, and Appendix A ++> (non-normative candidate notes tracking every open item):** the ++> `coulomb` org's `target-revenue` repository, ++> `specs/TargetRevenueSourceLicense-V1C1.md` — this file is the operative ++> legal text (Preamble through Section 11) only; the canonical document ++> is authoritative if this copy and that document ever diverge. ++ ++--- ++ ++## Preamble ++ ++This Target Revenue Source License ("**License**") governs the Software identified in the applicable Phase Manifest. It implements the Target Revenue Framework: a defined development Phase accumulates Development Credit and Remission Credit against an immutable Initial Target until the Milestone Release automatically and irrevocably converts to a declared permissive Future License. ++ ++Commercial beneficiaries fund the creation and early availability of a software improvement; once the declared target is satisfied, the governed release becomes permissively open source. ++ ++## 1. Definitions ++ ++Capitalized terms used in this License have the meanings given below. Where a term is also defined in the Phase Manifest or Target Ledger for a specific Phase, the Phase Manifest and Target Ledger govern the *values* (amounts, dates, identifiers) and this License governs the *legal effect* of those values — the two must not be read as conflicting definitions of the same concept. ++ ++**"Commercial Entitlement"** means a right, purchased or otherwise granted under a Commercial Use Agreement, to make Commercial Use of the Software during a Phase. ++ ++**"Commercial Use"** means billing, invoicing, or otherwise charging any customer a fee, subscription, license fee, or other consideration for or in connection with use of the Software, at any time before the Conversion Event for the applicable Phase, regardless of whether the person or organization billed would otherwise qualify for Noncommercial Use. Commercial Use occurs by virtue of such billing alone, whether or not the resulting payment is registered with the Trust Service; in particular, billing a customer for pre-conversion use of the Software without recording the corresponding payment in the applicable Phase's Target Ledger is Commercial Use without a valid Commercial Entitlement — a violation of Section 3, addressed under Section 7 and, where applicable, the Enforcement Network described in the canonical repository's `specs/EnforcementNetworkConcept.md`. ++ ++**"Commercial Use Agreement"** means the separate agreement, referenced by the applicable Phase Manifest, under which a Commercial Entitlement is purchased or granted. This License does not itself set pricing, metering, or payment terms — those are governed by the Commercial Use Agreement. ++ ++**"Conversion Event"** means the moment the Outstanding Target for a Phase reaches zero, as computed from the Phase Manifest and Target Ledger per the Target Ledger Specification. The Conversion Event occurs automatically and is not conditioned on any declaration, attestation, or other act by the Licensor or any Trust Service. ++ ++**"Development Credit"** means the portion of a collected and settled payment explicitly allocated toward satisfying the Initial Target of a specific Phase, as recorded in that Phase's Target Ledger. ++ ++**"Future License"** means the permissive license identified in the applicable Phase Manifest, being either the MIT License or the Apache License, Version 2.0, which applies to the Milestone Release upon the Conversion Event. ++ ++**"Initial Target"** means the immutable monetary target declared for a Phase in its Phase Manifest. ++ ++**"Licensor"** means **Binky Hedgehog GmbH**, the party that publishes the Phase Manifest and holds the rights necessary to grant this License and the Future License for the Milestone Release. ++ ++**"Milestone Release"** means the precisely identified software release designated in the applicable Phase Manifest, identified by an immutable source revision, release artifact, or cryptographic digest. ++ ++**"Noncommercial Use"** means use of the Software for personal purposes, private study, hobby or amateur projects; use by any charitable organization, educational institution, public research organization, or government institution acting in a non-revenue-generating capacity; or other use of a materially similar character. ++ ++**"Outstanding Target"** means, at any time, `max(0, Initial Target − cumulative Development Credit − cumulative Remission Credit)` for a Phase, as computed from that Phase's Target Ledger. ++ ++**"Phase"** means a bounded development undertaking governed by one Initial Target, one Milestone Release, one degeneration policy, and one Future License declaration, as declared in a Phase Manifest. ++ ++**"Phase Manifest"** means the published, immutable declaration identifying a Phase, its Milestone Release, Initial Target, Future License, degeneration policy, and Target Ledger location, as specified in the Phase Manifest Specification. ++ ++**"Remission Credit"** means a transparent, non-revenue reduction of a Phase's Outstanding Target, generated under that Phase's published degeneration policy and recorded in the Target Ledger. ++ ++**"Settled Payment"** means a payment that has cleared through its payment processor and is no longer subject to reversal in the ordinary course (chargeback, dispute, or equivalent), as further specified by the applicable Commercial Use Agreement or monetization extension. ++ ++**"Software"** means the source code, object code, and associated documentation of the Milestone Release identified in the applicable Phase Manifest. ++ ++**"Target Ledger"** means the append-only record of Development Credit, Remission Credit, and correction entries for a Phase, as specified in the Target Ledger Specification. ++ ++**"You"** or **"Licensee"** means the individual or entity exercising rights under this License. ++ ++## 2. Grant of Rights for Noncommercial Use ++ ++Subject to the terms of this License, the Licensor grants You a worldwide, royalty-free, non-exclusive license, during the applicable Phase, to: ++ ++(a) use, reproduce, and study the Software for any Noncommercial Use; ++ ++(b) modify the Software and create derivative works of it for any Noncommercial Use; and ++ ++(c) redistribute the Software and Your modifications, in source or object form, for any Noncommercial Use, provided that You include this License, unmodified, with any such redistribution, and that You do not remove or alter any copyright, patent, trademark, or attribution notices contained in the Software. ++ ++This grant does not extend to Commercial Use. Commercial Use requires a Commercial Entitlement under Section 3. ++ ++## 3. Commercial Use ++ ++You may not make Commercial Use of the Software during the applicable Phase unless You hold a valid, current Commercial Entitlement under a Commercial Use Agreement with the Licensor covering the applicable Phase. A Commercial Entitlement granted under one Phase's Commercial Use Agreement does not extend to a later Phase's Milestone Release unless the Commercial Use Agreement expressly says so. ++ ++This Section 3 states the existence and boundary of the commercial-use restriction. It does not itself set pricing, invoicing, metering, audit rights, or payment terms — those are governed exclusively by the applicable Commercial Use Agreement. ++ ++## 4. Patent License ++ ++Subject to the terms of this License, each contributor to the Software grants You, during the applicable Phase and solely to the extent of rights granted under Sections 2 and 3, a perpetual (subject to the termination below), worldwide, non-exclusive, no-charge, royalty-free patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Software, limited to those patent claims licensable by that contributor that are necessarily infringed by their contribution(s) alone or by combination of their contribution(s) with the Software. ++ ++If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software or a contribution incorporated within it constitutes direct or contributory patent infringement, then any patent licenses granted to You under this Section 4 for the Software shall terminate as of the date such litigation is filed. ++ ++## 5. Automatic Conversion to the Future License ++ ++**5.1 Automatic effect.** Upon the Conversion Event for a Phase, the rights and restrictions in Sections 3 (Commercial Use) of this License, as they apply to that Phase's Milestone Release, terminate automatically. In their place, the Milestone Release is licensed under the Future License identified in that Phase's Phase Manifest, effective as of the Conversion Event, without any further act, declaration, or attestation required by the Licensor, any Trust Service, or any other party. ++ ++**5.2 Irrevocability.** Once a valid Conversion Event has occurred for a Phase, no subsequent refund, chargeback, accounting correction, dispute, or termination of this License for an unrelated breach shall revoke, suspend, or otherwise impair the Future License grant for that Phase's Milestone Release. Any shortfall or dispute arising after a Conversion Event is a commercial or accounting matter between the relevant parties and does not reinstate a commercial-use restriction over already-converted Software. ++ ++**5.3 Prior freedom preserved.** A later Phase covering subsequent improvements to the Software does not restrict, withdraw, or otherwise affect the rights granted under the Future License for an earlier Phase's already-converted Milestone Release. ++ ++**5.4 Evidence, not cause.** A Trust Service may publish a Conversion Attestation documenting a Conversion Event. Such an attestation is evidence that the Conversion Event occurred; it is not a condition of, and its absence or delay does not postpone, the automatic effect described in Section 5.1. Any person may independently verify whether a Conversion Event has occurred directly from the Phase Manifest and Target Ledger. ++ ++## 6. Successive Phases ++ ++The Licensor may declare a new Phase covering subsequent improvements to the Software following a Milestone Release's Conversion Event. Each Phase is independently governed by its own Phase Manifest, Initial Target, degeneration policy, and Target Ledger. Nothing in a later Phase's Phase Manifest may be construed to reduce or withdraw rights already granted under Section 5 for an earlier Phase's Milestone Release. ++ ++## 7. Term and Termination ++ ++**7.1 Term.** This License applies to the Software for the duration of the applicable Phase, and, for the Milestone Release, indefinitely following that Phase's Conversion Event under the Future License. ++ ++**7.2 Termination for breach.** If You breach Section 3 (Commercial Use) or Section 2(c) (redistribution notice requirement), the Licensor may terminate this License as to You. Before such termination becomes effective, the Licensor shall provide You written notice of the breach; if You cure the breach within thirty (30) days of that notice, this License continues in effect. A second breach of the same provision within twelve (12) months may be terminated immediately without a further cure opportunity. ++ ++**7.3 Effect of termination.** Termination under this Section 7 affects only Your rights under Sections 2 and 3 for the Phase in which the breach occurred. It does not affect any rights already vested under Section 5 (Automatic Conversion) for a Milestone Release whose Conversion Event has already occurred, per Section 5.2. ++ ++**7.4 Public record of breach and resolution.** The Licensor shall cause the Trust Service to publish, as part of the public record for the affected Phase, notice of: (a) any breach notice issued under Section 7.2, stating the general nature of the breach and the date of notice; (b) whether the breach was cured within the applicable cure period, and the date of cure; and (c) any termination determination made under this Section 7, including its effective date and scope. This public record exists to give the ecosystem a transparent, verifiable conformity signal for the Phase, distinct from and in addition to the Development Credit and Remission Credit facts already published under Section 5.4 and the Target Ledger Specification. ++ ++A breach that You dispute, and that has not been finally determined, shall be recorded as **alleged**; it shall be recorded as **determined** only once the cure period has run without cure, or the dispute has been resolved against You under the applicable Commercial Use Agreement's dispute process, if any. The Trust Service shall update the record promptly upon resolution in either direction. Recording an alleged or determined breach under this Section 7.4 is a ministerial act of publishing the Licensor's determination (or a dispute process's outcome); it does not give the Trust Service discretionary authority to decide whether a breach occurred, consistent with Section 5.4's evidence-not-cause principle. ++ ++Whether, and under what conditions, the public record identifies a Commercial Entitlement holder by name is governed exclusively by the applicable Commercial Use Agreement, which the Licensor and that Commercial Entitlement holder negotiate and agree to directly. This License does not itself set a naming default. Where no Commercial Use Agreement addresses the question, or where the affected party has no Commercial Use Agreement at all (for example, a Section 2(c) breach by a Noncommercial Use licensee), the public record states the Phase and breach category only, without naming the party. ++ ++## 8. Disclaimer of Warranty ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE LICENSOR DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR-FREE OR THAT ANY PHASE WILL REACH ITS CONVERSION EVENT. ++ ++## 9. Limitation of Liability ++ ++IN NO EVENT SHALL THE LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EXCEPT TO THE EXTENT SUCH LIMITATION IS PROHIBITED BY APPLICABLE LAW. ++ ++## 10. Trademarks ++ ++This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary attribution. ++ ++## 11. General Provisions ++ ++**11.1 Governing law and venue.** Adopted for alpha/beta 2026-07-29: disputes arising under this License shall be resolved by binding arbitration, seated at a neutral, arbitration-mature venue (Singapore or London are the two candidates concretely supported by current research), rather than by litigation in a national court. The specific arbitral institution and substantive governing law remain a per-deployment blank pending final selection; they are not fixed by this candidate. See the canonical repository's `history/260729-TRSL-Jurisdiction-Synthesis.md` §2. ++ ++**11.2 Severability.** If any provision of this License is held unenforceable, the remaining provisions remain in full force, and the unenforceable provision shall be reformed to the minimum extent necessary to make it enforceable. ++ ++**11.3 No waiver.** Failure to enforce any provision of this License is not a waiver of future enforcement of that or any other provision. ++ ++**11.4 Entire agreement (as to licensing).** This License, together with the applicable Phase Manifest and, where applicable, the Commercial Use Agreement, constitutes the entire agreement between You and the Licensor regarding the Software's licensing terms. Operations, service, and consulting arrangements are governed by separate agreements, if any, and are not part of this License. ++ ++**11.5 Definitions control.** Marketing materials, documentation, or other non-normative communications about the Software must not describe pre-Conversion-Event Software as "Open Source," "free software," or "open core." Pre-conversion Noncommercial Use is **source-available**; pre-conversion Commercial Use requires a **Commercial Entitlement**; only post-conversion Software may be described as Open Source, under the Future License. ++ ++--- ++ ++**No Phase is currently declared for this repository under this License.** Until a Phase Manifest is published and registered with the Trust Service for a Milestone Release in this repository, Sections 2–7 above have no operative subject matter here — this License establishes the governing framework in advance of that declaration, consistent with the org-wide rollout decision recorded in `target-revenue`'s `workplans/TREV-WP-0008-governance-and-pilot-rollout.md`. +--- /dev/null ++++ b/third_party/access-engine-pep/go.mod +@@ -0,0 +1,3 @@ ++module github.com/netkingdom/access-engine ++ ++go 1.24 +--- /dev/null ++++ b/third_party/access-engine-pep/internal/sign/sign.go +@@ -0,0 +1,95 @@ ++// Package sign attaches a detached Ed25519 signature to a decision envelope ++// (FLEX-WP-0024). The signed material is json.Marshal of the envelope with ++// the signature field omitted. ++package sign ++ ++import ( ++ "bytes" ++ "crypto/ed25519" ++ "encoding/base64" ++ "encoding/json" ++ "fmt" ++ "io" ++ ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++const AlgorithmEd25519 = "ed25519" ++ ++// CanonicalBytes is the exact payload that is signed and verified: the ++// envelope as emitted, with signature absent. ++func CanonicalBytes(envelope api.DecisionEnvelope) ([]byte, error) { ++ envelope.Signature = nil ++ data, err := json.Marshal(envelope) ++ if err != nil { ++ return nil, fmt.Errorf("marshal canonical envelope: %w", err) ++ } ++ return data, nil ++} ++ ++func Sign(envelope *api.DecisionEnvelope, private ed25519.PrivateKey, kid string) error { ++ if envelope == nil { ++ return fmt.Errorf("envelope is required") ++ } ++ if len(private) != ed25519.PrivateKeySize { ++ return fmt.Errorf("ed25519 private key has length %d", len(private)) ++ } ++ if kid == "" { ++ return fmt.Errorf("kid is required") ++ } ++ payload, err := CanonicalBytes(*envelope) ++ if err != nil { ++ return err ++ } ++ sig := ed25519.Sign(private, payload) ++ envelope.Signature = &api.EnvelopeSignature{ ++ Mode: api.EnvelopeSignatureSigned, ++ Alg: AlgorithmEd25519, ++ Kid: kid, ++ Value: base64.RawURLEncoding.EncodeToString(sig), ++ } ++ return nil ++} ++ ++func Verify(envelope api.DecisionEnvelope, keys map[string]ed25519.PublicKey) error { ++ sig := envelope.Signature ++ if sig == nil || sig.Mode != api.EnvelopeSignatureSigned { ++ return fmt.Errorf("envelope is not signed") ++ } ++ if sig.Alg != AlgorithmEd25519 { ++ return fmt.Errorf("unsupported signature alg %q", sig.Alg) ++ } ++ pub, ok := keys[sig.Kid] ++ if !ok { ++ return fmt.Errorf("unknown signature kid %q", sig.Kid) ++ } ++ raw, err := base64.RawURLEncoding.DecodeString(sig.Value) ++ if err != nil { ++ return fmt.Errorf("decode signature: %w", err) ++ } ++ payload, err := CanonicalBytes(envelope) ++ if err != nil { ++ return err ++ } ++ if !ed25519.Verify(pub, payload, raw) { ++ return fmt.Errorf("signature does not match envelope") ++ } ++ return nil ++} ++ ++// DecodeEnvelope preserves JSON numeric kinds/lexemes emitted by task inputs. ++// Legacy producer envelopes remain byte-compatible. It does not invent another ++// signature canonicalization: CanonicalBytes still calls typed json.Marshal. ++func DecodeEnvelope(raw []byte) (api.DecisionEnvelope, error) { ++ var envelope api.DecisionEnvelope ++ decoder := json.NewDecoder(bytes.NewReader(raw)) ++ decoder.UseNumber() ++ decoder.DisallowUnknownFields() ++ if err := decoder.Decode(&envelope); err != nil { ++ return envelope, err ++ } ++ if _, err := decoder.Token(); err != io.EOF { ++ return api.DecisionEnvelope{}, fmt.Errorf("trailing envelope data") ++ } ++ return envelope, nil ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/canonical.go +@@ -0,0 +1,699 @@ ++package api ++ ++import ( ++ "crypto/sha256" ++ "encoding/hex" ++ "encoding/json" ++ "fmt" ++ "strings" ++ "time" ++) ++ ++// ProtectedSystemManifest describes a system that delegates authorization to ++// flex-auth. ++type ProtectedSystemManifest struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Description string `json:"description,omitempty" yaml:"description,omitempty"` ++ ClientBinding *ClientBinding `json:"client_binding,omitempty" yaml:"client_binding,omitempty"` ++ ResourceTypes []ResourceType `json:"resource_types,omitempty" yaml:"resource_types,omitempty"` ++ Actions []ActionDefinition `json:"actions,omitempty" yaml:"actions,omitempty"` ++ CaringProfiles []string `json:"caring_profiles,omitempty" yaml:"caring_profiles,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// ClientBinding names the key-cape client a protected system's users sign in ++// through. It is descriptive only: the readback registry projection lists it, ++// and it never decides an allow (FLEX-DEC-2026-019). ++type ClientBinding struct { ++ Issuer string `json:"issuer" yaml:"issuer"` ++ ClientID string `json:"client_id" yaml:"client_id"` ++} ++ ++// ResourceType describes a resource namespace entry owned by a protected system. ++type ResourceType struct { ++ Name string `json:"name" yaml:"name"` ++ ParentTypes []string `json:"parent_types,omitempty" yaml:"parent_types,omitempty"` ++ ScopeLevel ScopeLevel `json:"scope_level,omitempty" yaml:"scope_level,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// ActionDefinition maps a protected-system action to CARING capabilities. ++type ActionDefinition struct { ++ Name string `json:"name" yaml:"name"` ++ Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// SubjectManifest declares subjects, groups, teams, and tenants for local ++// registry loading. ++type SubjectManifest struct { ++ ID string `json:"id" yaml:"id"` ++ Subjects []Subject `json:"subjects,omitempty" yaml:"subjects,omitempty"` ++ Groups []Group `json:"groups,omitempty" yaml:"groups,omitempty"` ++ Teams []Team `json:"teams,omitempty" yaml:"teams,omitempty"` ++ Tenants []Tenant `json:"tenants,omitempty" yaml:"tenants,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Subject is a human, service, automation, agent, or other acting identity. ++type Subject struct { ++ ID string `json:"id" yaml:"id"` ++ Type SubjectType `json:"type" yaml:"type"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ OrganizationRelation OrganizationRelation `json:"organization_relation,omitempty" yaml:"organization_relation,omitempty"` ++ Roles []CanonicalRole `json:"roles,omitempty" yaml:"roles,omitempty"` ++ Groups []string `json:"groups,omitempty" yaml:"groups,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Claims map[string]any `json:"claims,omitempty" yaml:"claims,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Group is an assignment convenience, not a canonical role. ++type Group struct { ++ ID string `json:"id" yaml:"id"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ Members []string `json:"members,omitempty" yaml:"members,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Team is a group-like ownership unit used by protected systems. ++type Team struct { ++ ID string `json:"id" yaml:"id"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ Members []string `json:"members,omitempty" yaml:"members,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Tenant is a structural isolation boundary. ++type Tenant struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// RelationshipFact records a relation between subjects, groups, teams, tenants, ++// and resources. ++type RelationshipFact struct { ++ ID string `json:"id" yaml:"id"` ++ System string `json:"system,omitempty" yaml:"system,omitempty"` ++ Subject string `json:"subject" yaml:"subject"` ++ Relation string `json:"relation" yaml:"relation"` ++ Object string `json:"object" yaml:"object"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"` ++ Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// PolicyPackageMetadata is the frontmatter contract for Rego-in-Markdown ++// policy packages. ++type PolicyPackageMetadata struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` ++ Version string `json:"version" yaml:"version"` ++ Status string `json:"status,omitempty" yaml:"status,omitempty"` ++ Package string `json:"package" yaml:"package"` ++ Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"` ++ Owner string `json:"owner,omitempty" yaml:"owner,omitempty"` ++ Fixtures []string `json:"fixtures,omitempty" yaml:"fixtures,omitempty"` ++ Caring CaringPolicyMetadata `json:"caring" yaml:"caring"` ++ Activation map[string]any `json:"activation,omitempty" yaml:"activation,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++ // AllowTTL is a Go duration (for example "15m") that bounds every allow ++ // this package produces. Omit to use DefaultAllowTTL. "none" or "0s" ++ // means no stated end; the engine denies those allows (§9.7.1). ++ AllowTTL string `json:"allow_ttl,omitempty" yaml:"allow_ttl,omitempty"` ++} ++ ++// CaringPolicyMetadata declares the CARING envelope a policy governs. ++type CaringPolicyMetadata struct { ++ Profile string `json:"profile" yaml:"profile"` ++ Enforce bool `json:"enforce,omitempty" yaml:"enforce,omitempty"` ++ CanonicalRoles []CanonicalRole `json:"canonical_roles,omitempty" yaml:"canonical_roles,omitempty"` ++ OrganizationRelations []OrganizationRelation `json:"organization_relations,omitempty" yaml:"organization_relations,omitempty"` ++ Scopes []CaringScope `json:"scopes,omitempty" yaml:"scopes,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// PolicyFixture binds a check request to an expected decision. ++type PolicyFixture struct { ++ ID string `json:"id" yaml:"id"` ++ Request CheckRequest `json:"request" yaml:"request"` ++ Expect DecisionExpectation `json:"expect" yaml:"expect"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// DecisionExpectation is the compact fixture expectation for policy tests. ++type DecisionExpectation struct { ++ Effect DecisionEffect `json:"effect" yaml:"effect"` ++ Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` ++ Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++} ++ ++// CheckRequest is the stable protected-system-facing decision request. ++type CheckRequest struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ CaringContext *CaringAccessDescriptor `json:"caring_context,omitempty" yaml:"caring_context,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++ ++ // Purpose is empty on every enforcement request. The readback pin sets it ++ // to PurposeReadback, and it is hashed into every request digest, so an ++ // advisory readback decision can never pass a PEP's §6.4.2 replay test for ++ // an enforcement request (FLEX-DEC-2026-019). /v1/check and /v1/batch_check ++ // refuse a request that carries it. ++ Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"` ++} ++ ++// PurposeReadback marks an advisory application-access readback decision. ++const PurposeReadback = "readback" ++ ++// BatchCheckRequest evaluates one subject/action against multiple resources. ++type BatchCheckRequest struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resources []ResourceRef `json:"resources" yaml:"resources"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++} ++ ++// SubjectRef is a normalized subject reference in request and decision shapes. ++type SubjectRef struct { ++ ID string `json:"id" yaml:"id"` ++ Type SubjectType `json:"type,omitempty" yaml:"type,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// ResourceRef is a normalized resource reference in request and decision shapes. ++type ResourceRef struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type,omitempty" yaml:"type,omitempty"` ++ System string `json:"system,omitempty" yaml:"system,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// DecisionEffect is the stable decision outcome vocabulary. ++type DecisionEffect string ++ ++const ( ++ DecisionEffectAllow DecisionEffect = "allow" ++ DecisionEffectDeny DecisionEffect = "deny" ++ DecisionEffectRedact DecisionEffect = "redact" ++ DecisionEffectAuditOnly DecisionEffect = "audit_only" ++ DecisionEffectNotApplicable DecisionEffect = "not_applicable" ++) ++ ++// DecisionEffects returns the complete effect vocabulary. Every effect is an ++// emission class in cadence.yaml; a test fails if a constant above is missing ++// here, so a new effect cannot ship unclassified (FLEX-WP-0031-T03). ++func DecisionEffects() []DecisionEffect { ++ return []DecisionEffect{ ++ DecisionEffectAllow, ++ DecisionEffectDeny, ++ DecisionEffectRedact, ++ DecisionEffectAuditOnly, ++ DecisionEffectNotApplicable, ++ } ++} ++ ++// Restricts reports whether an effect withholds authority. This classification ++// does not exempt an effect from required durable emission (GH-DEC-2026-024). ++func (e DecisionEffect) Restricts() bool { ++ switch e { ++ case DecisionEffectDeny, DecisionEffectRedact, DecisionEffectNotApplicable: ++ return true ++ } ++ return false ++} ++ ++// DecisionRecordContractV1 is the published decision-record contract identifier. ++const DecisionRecordContractV1 = "flex-auth.decision-record.v1" ++ ++// DecisionEnvelope is the stable response produced by standalone and delegated ++// evaluators. It is flex-auth's published decision-record contract (§17). ++type DecisionEnvelope struct { ++ ID string `json:"id" yaml:"id"` ++ ContractVersion string `json:"contract_version,omitempty" yaml:"contract_version,omitempty"` ++ RequestID string `json:"request_id,omitempty" yaml:"request_id,omitempty"` ++ Effect DecisionEffect `json:"effect" yaml:"effect"` ++ Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` ++ MatchedPolicyVersion string `json:"matched_policy_version,omitempty" yaml:"matched_policy_version,omitempty"` ++ MatchedRule string `json:"matched_rule,omitempty" yaml:"matched_rule,omitempty"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Binding *DecisionBinding `json:"binding,omitempty" yaml:"binding,omitempty"` ++ Lifetime *DecisionLifetime `json:"lifetime,omitempty" yaml:"lifetime,omitempty"` ++ Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"` ++ Diagnostics map[string]any `json:"diagnostics,omitempty" yaml:"diagnostics,omitempty"` ++ Provenance DecisionProvenance `json:"provenance" yaml:"provenance"` ++ Signature *EnvelopeSignature `json:"signature,omitempty" yaml:"signature,omitempty"` ++ Caring *CaringDecisionMetadata `json:"caring,omitempty" yaml:"caring,omitempty"` ++} ++ ++// EnvelopeSignatureMode is how the responder authenticates the envelope. ++type EnvelopeSignatureMode string ++ ++const ( ++ EnvelopeSignatureSigned EnvelopeSignatureMode = "signed" ++ EnvelopeSignatureUnsigned EnvelopeSignatureMode = "unsigned" ++) ++ ++// EnvelopeSignature is a detached signature over the canonical envelope with ++// this field omitted (FLEX-WP-0024). It is not decision material and is not ++// part of request_digest. ++type EnvelopeSignature struct { ++ Mode EnvelopeSignatureMode `json:"mode" yaml:"mode"` ++ Alg string `json:"alg,omitempty" yaml:"alg,omitempty"` ++ Kid string `json:"kid,omitempty" yaml:"kid,omitempty"` ++ Value string `json:"value,omitempty" yaml:"value,omitempty"` ++} ++ ++func UnsignedEnvelopeSignature() *EnvelopeSignature { ++ return &EnvelopeSignature{Mode: EnvelopeSignatureUnsigned} ++} ++ ++// DecisionLifetimeKind identifies how an allow ends. ++type DecisionLifetimeKind string ++ ++const ( ++ DecisionLifetimeTTL DecisionLifetimeKind = "ttl" ++) ++ ++// DefaultAllowTTL is the engine default when a policy package omits allow_ttl. ++const DefaultAllowTTL = 15 * time.Minute ++ ++// ReasonAllowLifetimeUnstated is the deny reason for an allow with no stated end. ++const ReasonAllowLifetimeUnstated = "allow_lifetime_unstated" ++ ++// DecisionLifetime bounds an allow (§9.7.1). flex-auth has no session concept, ++// so the first honest shape is a policy-package-declared TTL. ++type DecisionLifetime struct { ++ Kind DecisionLifetimeKind `json:"kind" yaml:"kind"` ++ TTL string `json:"ttl,omitempty" yaml:"ttl,omitempty"` ++ NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"` ++ ExpiresAt string `json:"expires_at" yaml:"expires_at"` ++} ++ ++// DecisionBinding is the exact normalized authorization request evaluated by ++// a decision. It lets a consumer verify structured action, target, actor, and ++// context fields without parsing reason or diagnostic prose. ++type DecisionBinding struct { ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ // Purpose is "readback" on an advisory readback decision and absent on an ++ // enforcement decision (FLEX-DEC-2026-019). ++ Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"` ++ RequestDigest string `json:"request_digest" yaml:"request_digest"` ++ ++ // ApprovalBindingDigest is RequestDigest computed with the approval claim ++ // removed from context. It exists to break a circularity: an approval's ++ // pdp_digest is recorded at issue time, but the request that later carries ++ // the claim inside its hashed context has a different RequestDigest by ++ // construction, so a claim can never name the request that carries it. ++ // ++ // This digest is stable across attaching the claim, so it is the value ++ // claim.binding.pdp_digest must equal (GH-DEC-2026-008). It is NOT a replay ++ // identity and must not be used as one: two requests differing only in ++ // which approval was presented share it, and the decision does not. ++ ApprovalBindingDigest string `json:"approval_binding_digest,omitempty" yaml:"approval_binding_digest,omitempty"` ++ ++ // SubmittedRequestDigest is RequestDigest over the request exactly as the ++ // consumer sent it, before the evaluator overlays registry facts onto the ++ // subject and resource. ++ // ++ // It exists because RequestDigest is not consumer-computable and was ++ // published as though it were. The evaluator hashes the ENRICHED request — ++ // subject.tenant, subject.attributes, and resource attributes the registry ++ // contributed — and the registry is flex-auth's, so a consumer recomputing ++ // the digest over what it sent gets a different value on every real allow. ++ // secrets-engine found this on its first live request (FLEX-DEC-2026-012). ++ // ++ // This is the digest a PEP compares for §6.4 obligation 2's replay test. ++ // Nothing is lost by hashing the pre-enrichment form: enrichment is a ++ // function of the request and the registry snapshot, and ++ // provenance.registry_snapshot_digest already pins the snapshot, so ++ // SubmittedRequestDigest together with that digest identifies the evaluated ++ // request completely. ++ SubmittedRequestDigest string `json:"submitted_request_digest,omitempty" yaml:"submitted_request_digest,omitempty"` ++} ++ ++// ApprovalContextKey is the context key carrying an approval-engine ++// approval-claim. It is excluded from ApprovalBindingDigest and from nothing ++// else. ++const ApprovalContextKey = "approval" ++ ++// requestDigestMaterial is the exact tuple hashed for §6.4.2 replay. Request ++// id, policy version, and caring_context are excluded: id is correlation, the ++// version is provenance, and caring_context is an input-claim digest. ++type requestDigestMaterial struct { ++ Tenant string `json:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject"` ++ Action string `json:"action"` ++ Resource ResourceRef `json:"resource"` ++ Context map[string]any `json:"context,omitempty"` ++ Purpose string `json:"purpose,omitempty"` ++} ++ ++// NewDecisionBinding returns a stable structured binding for the exact request ++// an evaluator consumed. ++// ++// Prefer NewDecisionBindingFor, which also records the digest of the request as ++// submitted. This form leaves SubmittedRequestDigest empty, which reads as "the ++// evaluator did not record one" rather than "the two are equal". ++func NewDecisionBinding(request CheckRequest) *DecisionBinding { ++ contextCopy := make(map[string]any, len(request.Context)) ++ for key, value := range request.Context { ++ contextCopy[key] = value ++ } ++ binding := &DecisionBinding{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: contextCopy, ++ Purpose: request.Purpose, ++ RequestDigest: RequestDigest(request), ++ } ++ if _, carries := request.Context[ApprovalContextKey]; carries { ++ binding.ApprovalBindingDigest = ApprovalBindingDigest(request) ++ } ++ return binding ++} ++ ++// NewDecisionBindingFor records the binding of the evaluated request together ++// with the digest of the request as submitted. ++// ++// The two arguments are the same request before and after the evaluator ++// overlaid registry facts. Where a request named nothing the registry knows they ++// are equal, and the field is emitted anyway: a consumer that only sees it on ++// enriched decisions would build a check that passes by absence. ++func NewDecisionBindingFor(evaluated, submitted CheckRequest) *DecisionBinding { ++ binding := NewDecisionBinding(evaluated) ++ binding.SubmittedRequestDigest = RequestDigest(submitted) ++ return binding ++} ++ ++// ApprovalBindingDigest is RequestDigest over the same material with the ++// approval claim removed from context. ++// ++// When a request carries no approval claim the two are identical, which is the ++// point: an approval issued against a claim-free Check records that Check's ++// request_digest, and the later claim-bearing request reproduces the same value ++// here. Comparing it to claim.binding.pdp_digest establishes correspondence by ++// identity rather than by translating between two action vocabularies ++// (GH-DEC-2026-008). ++func ApprovalBindingDigest(request CheckRequest) string { ++ if _, carries := request.Context[ApprovalContextKey]; !carries { ++ return RequestDigest(request) ++ } ++ stripped := make(map[string]any, len(request.Context)) ++ for key, value := range request.Context { ++ if key == ApprovalContextKey { ++ continue ++ } ++ stripped[key] = value ++ } ++ if len(stripped) == 0 { ++ stripped = nil ++ } ++ return CanonicalDigest(requestDigestMaterial{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: stripped, ++ Purpose: request.Purpose, ++ }) ++} ++ ++// RequestDigest is the mechanical §6.4.2 replay test: SHA-256 over canonical ++// JSON of tenant, subject, action, resource, context, and purpose. Purpose is ++// omitted when empty, so enforcement digests are unchanged by its existence. ++func RequestDigest(request CheckRequest) string { ++ return CanonicalDigest(requestDigestMaterial{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: request.Context, ++ Purpose: request.Purpose, ++ }) ++} ++ ++// CanonicalDigest returns "sha256:" plus the hex SHA-256 of canonical JSON. ++// encoding/json sorts map keys, so two equal Go values agree. ++func CanonicalDigest(value any) string { ++ data, err := json.Marshal(value) ++ if err != nil { ++ sum := sha256.Sum256(nil) ++ return "sha256:" + hex.EncodeToString(sum[:]) ++ } ++ sum := sha256.Sum256(data) ++ return "sha256:" + hex.EncodeToString(sum[:]) ++} ++ ++// InputClaimDigests hashes the request-time claim classes the evaluator joined. ++func InputClaimDigests(request CheckRequest) map[string]string { ++ digests := make(map[string]string) ++ if len(request.Context) > 0 { ++ digests["context"] = CanonicalDigest(request.Context) ++ } ++ if request.CaringContext != nil { ++ digests["caring_context"] = CanonicalDigest(request.CaringContext) ++ } ++ if len(digests) == 0 { ++ return nil ++ } ++ return digests ++} ++ ++// DecisionCompletion carries evaluator-side inputs used to finish an envelope. ++type DecisionCompletion struct { ++ AllowTTL string ++ Now time.Time ++} ++ ++// CompleteDecision stamps contract version, input-claim digests, decision time, ++// and an explicit allow lifetime. An allow with no stated end becomes a deny. ++func CompleteDecision(envelope *DecisionEnvelope, request CheckRequest, completion DecisionCompletion) { ++ if envelope == nil { ++ return ++ } ++ if envelope.ContractVersion == "" { ++ envelope.ContractVersion = DecisionRecordContractV1 ++ } ++ if envelope.Provenance.InputClaimDigests == nil { ++ envelope.Provenance.InputClaimDigests = InputClaimDigests(request) ++ } ++ ApplyAllowLifetime(envelope, completion.AllowTTL, completion.Now) ++} ++ ++// ParseAllowTTL resolves a package-declared TTL. ok is false when the allow ++// would have no stated end. Invalid strings return an error so package ++// validation can reject them. ++func ParseAllowTTL(declared string) (time.Duration, error) { ++ trimmed := strings.TrimSpace(declared) ++ if trimmed == "" { ++ return DefaultAllowTTL, nil ++ } ++ if strings.EqualFold(trimmed, "none") { ++ return 0, nil ++ } ++ ttl, err := time.ParseDuration(trimmed) ++ if err != nil { ++ return 0, fmt.Errorf("allow_ttl %q is not a Go duration: %w", declared, err) ++ } ++ if ttl <= 0 { ++ return 0, nil ++ } ++ return ttl, nil ++} ++ ++// ApplyAllowLifetime sets DecisionTime and, for allows, an explicit TTL. A ++// missing or zero TTL denies the allow rather than mint a standing grant. ++func ApplyAllowLifetime(envelope *DecisionEnvelope, declaredTTL string, now time.Time) { ++ if envelope == nil { ++ return ++ } ++ if now.IsZero() { ++ now = time.Now().UTC() ++ } else { ++ now = now.UTC() ++ } ++ if envelope.Provenance.DecisionTime == "" { ++ envelope.Provenance.DecisionTime = now.Format(time.RFC3339) ++ } ++ if envelope.Effect != DecisionEffectAllow { ++ return ++ } ++ ttl, err := ParseAllowTTL(declaredTTL) ++ if err != nil || ttl <= 0 { ++ if envelope.Diagnostics == nil { ++ envelope.Diagnostics = map[string]any{} ++ } ++ if envelope.Reason != "" { ++ envelope.Diagnostics["unstated_allow_reason"] = envelope.Reason ++ } ++ envelope.Effect = DecisionEffectDeny ++ envelope.Reason = ReasonAllowLifetimeUnstated ++ envelope.MatchedRule = ReasonAllowLifetimeUnstated ++ envelope.Lifetime = nil ++ return ++ } ++ display := strings.TrimSpace(declaredTTL) ++ if display == "" { ++ display = "15m" ++ } ++ envelope.Lifetime = &DecisionLifetime{ ++ Kind: DecisionLifetimeTTL, ++ TTL: display, ++ NotBefore: now.Format(time.RFC3339), ++ ExpiresAt: now.Add(ttl).Format(time.RFC3339), ++ } ++} ++ ++// ActionAuthorizationStatus is the lifecycle state of a durable authorization. ++type ActionAuthorizationStatus string ++ ++const ( ++ ActionAuthorizationPending ActionAuthorizationStatus = "pending" ++ ActionAuthorizationApproved ActionAuthorizationStatus = "approved" ++ ActionAuthorizationDenied ActionAuthorizationStatus = "denied" ++ ActionAuthorizationSuperseded ActionAuthorizationStatus = "superseded" ++ ActionAuthorizationExpired ActionAuthorizationStatus = "expired" ++ ActionAuthorizationRevoked ActionAuthorizationStatus = "revoked" ++) ++ ++// ActionAuthorization joins a durable approval lifecycle to one exact ++// flex-auth request and decision. Storage and approval collection remain the ++// responsibility of the organizational decision authority. ++type ActionAuthorization struct { ++ SchemaVersion string `json:"schema_version" yaml:"schema_version"` ++ ID string `json:"id" yaml:"id"` ++ Status ActionAuthorizationStatus `json:"status" yaml:"status"` ++ SupersededBy string `json:"superseded_by,omitempty" yaml:"superseded_by,omitempty"` ++ Request CheckRequest `json:"request" yaml:"request"` ++ Validity ActionAuthorizationValidity `json:"validity" yaml:"validity"` ++ Approvals ActionAuthorizationApprovals `json:"approvals" yaml:"approvals"` ++ Decision DecisionEnvelope `json:"decision" yaml:"decision"` ++ Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"` ++} ++ ++// ActionAuthorizationValidity bounds execution of an approved action. ++type ActionAuthorizationValidity struct { ++ NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"` ++ ExpiresAt string `json:"expires_at" yaml:"expires_at"` ++} ++ ++// ActionAuthorizationApprovals declares the approval threshold and evidence. ++type ActionAuthorizationApprovals struct { ++ RequiredCount int `json:"required_count" yaml:"required_count"` ++ Entries []ActionAuthorizationApprovalEntry `json:"entries" yaml:"entries"` ++} ++ ++// ActionAuthorizationApprovalEntry is one authenticated approver's evidence. ++type ActionAuthorizationApprovalEntry struct { ++ SubjectID string `json:"subject_id" yaml:"subject_id"` ++ ApprovedAt string `json:"approved_at" yaml:"approved_at"` ++ Assurance string `json:"assurance,omitempty" yaml:"assurance,omitempty"` ++ EvidenceRef string `json:"evidence_ref,omitempty" yaml:"evidence_ref,omitempty"` ++} ++ ++// Obligation describes a follow-up behavior required by a decision. ++type Obligation struct { ++ Type string `json:"type" yaml:"type"` ++ Parameters map[string]any `json:"parameters,omitempty" yaml:"parameters,omitempty"` ++} ++ ++// DecisionProvenance captures evaluator and policy provenance. ++type DecisionProvenance struct { ++ Evaluator string `json:"evaluator" yaml:"evaluator"` ++ Mode string `json:"mode" yaml:"mode"` ++ PolicyPackage string `json:"policy_package,omitempty" yaml:"policy_package,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++ PolicyPackageDigest string `json:"policy_package_digest,omitempty" yaml:"policy_package_digest,omitempty"` ++ RegistrySnapshotDigest string `json:"registry_snapshot_digest,omitempty" yaml:"registry_snapshot_digest,omitempty"` ++ DirectoryETag string `json:"directory_etag,omitempty" yaml:"directory_etag,omitempty"` ++ InputClaimDigests map[string]string `json:"input_claim_digests,omitempty" yaml:"input_claim_digests,omitempty"` ++ DecisionTime string `json:"decision_time,omitempty" yaml:"decision_time,omitempty"` ++ ApprovalObservation *ApprovalObservationProvenance `json:"approval_observation,omitempty" yaml:"approval_observation,omitempty"` ++ Caller *CallerProvenance `json:"caller,omitempty" yaml:"caller,omitempty"` ++} ++ ++// ApprovalObservationProvenance binds fresh server-owned fact evidence without ++// rewriting the original carried claim/context or either PDP request digest. ++type ApprovalObservationProvenance struct { ++ SourceID string `json:"source_id"` ++ SourceRevision string `json:"source_revision"` ++ ConfigurationDigest string `json:"configuration_digest"` ++ ClaimDigest string `json:"claim_digest"` ++ NativeBindingDigest string `json:"native_binding_digest"` ++ ObservedAt string `json:"observed_at"` ++ NotAfter string `json:"not_after"` ++ ValidatedAt string `json:"validated_at"` ++} ++ ++// CallerProvenance records how the request was authenticated to flex-auth. ++// It is not decision material: the same request from a different authenticated ++// caller must decide identically. Mode is required. Under disabled the object ++// is {"mode":"disabled"} with no principal (FLEX-DEC-2026-009). ++type CallerProvenance struct { ++ Mode string `json:"mode" yaml:"mode"` ++ Principal string `json:"principal,omitempty" yaml:"principal,omitempty"` ++ Audience string `json:"audience,omitempty" yaml:"audience,omitempty"` ++ NotAfter string `json:"not_after,omitempty" yaml:"not_after,omitempty"` ++} ++ ++// CaringDecisionMetadata carries CARING descriptor and conformance details in ++// a decision envelope. ++type CaringDecisionMetadata struct { ++ Profile string `json:"profile" yaml:"profile"` ++ Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ RestrictionsEvaluated []Restriction `json:"restrictions_evaluated,omitempty" yaml:"restrictions_evaluated,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++ ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++} ++ ++// AuditEvent is the local log shape for decisions and exposure events. ++type AuditEvent struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type" yaml:"type"` ++ DecisionID string `json:"decision_id,omitempty" yaml:"decision_id,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Resource ResourceRef `json:"resource,omitempty" yaml:"resource,omitempty"` ++ Action string `json:"action,omitempty" yaml:"action,omitempty"` ++ Effect DecisionEffect `json:"effect,omitempty" yaml:"effect,omitempty"` ++ Timestamp string `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` ++ ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/caring.go +@@ -0,0 +1,335 @@ ++package api ++ ++// CaringProfileCaring040RC2 is the executable profile identifier for the ++// CARING 0.4.0-RC2 standard pinned by flex-auth. ++const CaringProfileCaring040RC2 = "caring-0.4.0-rc2" ++ ++// SubjectType is the CARING subject dimension. ++type SubjectType string ++ ++const ( ++ SubjectTypeHuman SubjectType = "Human" ++ SubjectTypeGroup SubjectType = "Group" ++ SubjectTypeOrganization SubjectType = "Organization" ++ SubjectTypeService SubjectType = "Service" ++ SubjectTypeAutomation SubjectType = "Automation" ++ SubjectTypeAgent SubjectType = "Agent" ++ SubjectTypeSystem SubjectType = "System" ++ SubjectTypeDevice SubjectType = "Device" ++ SubjectTypeProcess SubjectType = "Process" ++ SubjectTypeAnonymous SubjectType = "Anonymous" ++ SubjectTypeUnknown SubjectType = "Unknown" ++) ++ ++// OrganizationRelation is the CARING organization-relation dimension. ++type OrganizationRelation string ++ ++const ( ++ OrganizationRelationVendor OrganizationRelation = "Vendor" ++ OrganizationRelationServiceProvider OrganizationRelation = "ServiceProvider" ++ OrganizationRelationDistributor OrganizationRelation = "Distributor" ++ OrganizationRelationConsultant OrganizationRelation = "Consultant" ++ OrganizationRelationCustomer OrganizationRelation = "Customer" ++ OrganizationRelationCommunity OrganizationRelation = "Community" ++ OrganizationRelationAuthority OrganizationRelation = "Authority" ++ OrganizationRelationUnknown OrganizationRelation = "Unknown" ++) ++ ++// CanonicalRole is the CARING lifecycle responsibility posture. ++type CanonicalRole string ++ ++const ( ++ CanonicalRoleCreator CanonicalRole = "Creator" ++ CanonicalRoleBuilder CanonicalRole = "Builder" ++ CanonicalRoleVerifier CanonicalRole = "Verifier" ++ CanonicalRoleMaintainer CanonicalRole = "Maintainer" ++ CanonicalRoleIntegrator CanonicalRole = "Integrator" ++ CanonicalRoleOperator CanonicalRole = "Operator" ++ CanonicalRoleManager CanonicalRole = "Manager" ++ CanonicalRoleCoach CanonicalRole = "Coach" ++ CanonicalRoleDoer CanonicalRole = "Doer" ++) ++ ++// ScopeLevel is the CARING scope ladder. ++type ScopeLevel string ++ ++const ( ++ ScopeLevelEcosystem ScopeLevel = "Ecosystem" ++ ScopeLevelProduct ScopeLevel = "Product" ++ ScopeLevelPlatform ScopeLevel = "Platform" ++ ScopeLevelCluster ScopeLevel = "Cluster" ++ ScopeLevelEnvironment ScopeLevel = "Environment" ++ ScopeLevelTenant ScopeLevel = "Tenant" ++ ScopeLevelNamespace ScopeLevel = "Namespace" ++ ScopeLevelDomain ScopeLevel = "Domain" ++ ScopeLevelWorkspace ScopeLevel = "Workspace" ++ ScopeLevelProject ScopeLevel = "Project" ++ ScopeLevelProcess ScopeLevel = "Process" ++ ScopeLevelDataset ScopeLevel = "Dataset" ++ ScopeLevelResource ScopeLevel = "Resource" ++ ScopeLevelSubresource ScopeLevel = "Subresource" ++ ScopeLevelRecord ScopeLevel = "Record" ++ ScopeLevelField ScopeLevel = "Field" ++ ScopeLevelAction ScopeLevel = "Action" ++) ++ ++// Plane is the CARING access-surface dimension. ++type Plane string ++ ++const ( ++ PlaneIntent Plane = "Intent" ++ PlaneBuild Plane = "Build" ++ PlaneRuntime Plane = "Runtime" ++ PlaneExecution Plane = "Execution" ++ PlaneConfiguration Plane = "Configuration" ++ PlaneData Plane = "Data" ++ PlaneIdentity Plane = "Identity" ++ PlanePolicy Plane = "Policy" ++ PlaneSecret Plane = "Secret" ++ PlaneAudit Plane = "Audit" ++ PlaneCommercial Plane = "Commercial" ++ PlaneCommunity Plane = "Community" ++) ++ ++// Capability is a CARING capability verb. ++type Capability string ++ ++const ( ++ CapabilityView Capability = "View" ++ CapabilityViewCollection Capability = "ViewCollection" ++ CapabilityObserve Capability = "Observe" ++ CapabilityCreate Capability = "Create" ++ CapabilityEditOwn Capability = "EditOwn" ++ CapabilityEditAssigned Capability = "EditAssigned" ++ CapabilityEditAny Capability = "EditAny" ++ CapabilityDeleteOwn Capability = "DeleteOwn" ++ CapabilityDeleteAny Capability = "DeleteAny" ++ CapabilityBulkDelete Capability = "BulkDelete" ++ CapabilitySubmit Capability = "Submit" ++ CapabilityComment Capability = "Comment" ++ CapabilityReview Capability = "Review" ++ CapabilityApprove Capability = "Approve" ++ CapabilityReject Capability = "Reject" ++ CapabilityPublish Capability = "Publish" ++ CapabilityArchive Capability = "Archive" ++ CapabilityRestore Capability = "Restore" ++ CapabilityExecute Capability = "Execute" ++ CapabilityConfigure Capability = "Configure" ++ CapabilityOperate Capability = "Operate" ++ CapabilityDeploy Capability = "Deploy" ++ CapabilityIntegrate Capability = "Integrate" ++ CapabilityGrant Capability = "Grant" ++ CapabilityRevoke Capability = "Revoke" ++ CapabilityDelegate Capability = "Delegate" ++ CapabilityImpersonate Capability = "Impersonate" ++ CapabilityExport Capability = "Export" ++ CapabilityImport Capability = "Import" ++ CapabilityReplicate Capability = "Replicate" ++ CapabilityEncrypt Capability = "Encrypt" ++ CapabilityDecrypt Capability = "Decrypt" ++ CapabilityMask Capability = "Mask" ++ CapabilityInspect Capability = "Inspect" ++ CapabilityAudit Capability = "Audit" ++ CapabilityOverride Capability = "Override" ++ CapabilityEscalate Capability = "Escalate" ++ CapabilityBind Capability = "Bind" ++ CapabilityUse Capability = "Use" ++) ++ ++// ExposureMode describes how much information becomes visible or extractable. ++type ExposureMode string ++ ++const ( ++ ExposureModeNone ExposureMode = "None" ++ ExposureModeMetadata ExposureMode = "Metadata" ++ ExposureModeMasked ExposureMode = "Masked" ++ ExposureModeAggregated ExposureMode = "Aggregated" ++ ExposureModeSynthetic ExposureMode = "Synthetic" ++ ExposureModePseudonymous ExposureMode = "Pseudonymous" ++ ExposureModeEncrypted ExposureMode = "Encrypted" ++ ExposureModePlaintext ExposureMode = "Plaintext" ++ ExposureModeSecretMaterial ExposureMode = "SecretMaterial" ++ ExposureModeExportable ExposureMode = "Exportable" ++ ExposureModeCrossTenantAggregate ExposureMode = "CrossTenantAggregate" ++) ++ ++// Condition is a CARING runtime or governance condition. ++type Condition string ++ ++const ( ++ ConditionMFARequired Condition = "MFARequired" ++ ConditionDeviceTrusted Condition = "DeviceTrusted" ++ ConditionNetworkTrusted Condition = "NetworkTrusted" ++ ConditionTicketRequired Condition = "TicketRequired" ++ ConditionTenantConsentRequired Condition = "TenantConsentRequired" ++ ConditionCustomerApprovalRequired Condition = "CustomerApprovalRequired" ++ ConditionDualApprovalRequired Condition = "DualApprovalRequired" ++ ConditionTimeLimited Condition = "TimeLimited" ++ ConditionBusinessHoursOnly Condition = "BusinessHoursOnly" ++ ConditionEmergencyOnly Condition = "EmergencyOnly" ++ ConditionTrainingRequired Condition = "TrainingRequired" ++ ConditionContractRequired Condition = "ContractRequired" ++ ConditionNDARequired Condition = "NDARequired" ++ ConditionPurposeBound Condition = "PurposeBound" ++ ConditionCaseBound Condition = "CaseBound" ++ ConditionEnvironmentBound Condition = "EnvironmentBound" ++ ConditionNamespaceBound Condition = "NamespaceBound" ++ ConditionPipelineBound Condition = "PipelineBound" ++ ConditionChangeWindowBound Condition = "ChangeWindowBound" ++ ConditionLogged Condition = "Logged" ++ ConditionRecorded Condition = "Recorded" ++ ConditionNotificationRequired Condition = "NotificationRequired" ++ ConditionPostReviewRequired Condition = "PostReviewRequired" ++ ConditionHumanReviewRequired Condition = "HumanReviewRequired" ++ ConditionPolicyReviewRequired Condition = "PolicyReviewRequired" ++ ConditionWorkloadIdentityRequired Condition = "WorkloadIdentityRequired" ++) ++ ++// LifecycleState describes why access exists now. ++type LifecycleState string ++ ++const ( ++ LifecycleStateDesign LifecycleState = "Design" ++ LifecycleStateBuild LifecycleState = "Build" ++ LifecycleStateTest LifecycleState = "Test" ++ LifecycleStateReview LifecycleState = "Review" ++ LifecycleStateRelease LifecycleState = "Release" ++ LifecycleStateOnboard LifecycleState = "Onboard" ++ LifecycleStateIntegrate LifecycleState = "Integrate" ++ LifecycleStateMigrate LifecycleState = "Migrate" ++ LifecycleStateOperate LifecycleState = "Operate" ++ LifecycleStateSupport LifecycleState = "Support" ++ LifecycleStateImprove LifecycleState = "Improve" ++ LifecycleStateDeprecate LifecycleState = "Deprecate" ++ LifecycleStateArchive LifecycleState = "Archive" ++ LifecycleStateIncident LifecycleState = "Incident" ++ LifecycleStateLegal LifecycleState = "Legal" ++ LifecycleStateTerminate LifecycleState = "Terminate" ++) ++ ++// Restriction is an overriding CARING deny or limiting policy effect. ++type Restriction string ++ ++const ( ++ RestrictionNoAccess Restriction = "NoAccess" ++ RestrictionSuspended Restriction = "Suspended" ++ RestrictionTerminated Restriction = "Terminated" ++ RestrictionQuarantined Restriction = "Quarantined" ++ RestrictionScopeExcluded Restriction = "ScopeExcluded" ++ RestrictionDataClassRestricted Restriction = "DataClassRestricted" ++ RestrictionLegalHold Restriction = "LegalHold" ++ RestrictionExportBlocked Restriction = "ExportBlocked" ++ RestrictionImpersonationBlocked Restriction = "ImpersonationBlocked" ++ RestrictionCrossTenantBlocked Restriction = "CrossTenantBlocked" ++ RestrictionSecretAccessBlocked Restriction = "SecretAccessBlocked" ++ RestrictionPolicyFrozen Restriction = "PolicyFrozen" ++ RestrictionEmergencyLocked Restriction = "EmergencyLocked" ++ RestrictionRiskDenied Restriction = "RiskDenied" ++ RestrictionExecutionBlocked Restriction = "ExecutionBlocked" ++ RestrictionWorkloadCreationBlocked Restriction = "WorkloadCreationBlocked" ++ RestrictionPrivilegeEscalationBlocked Restriction = "PrivilegeEscalationBlocked" ++) ++ ++// ExposureEventType is a CARING exceptional or irregular access class. ++type ExposureEventType string ++ ++const ( ++ ExposureEventSupport ExposureEventType = "X-Support" ++ ExposureEventBreakGlass ExposureEventType = "X-BreakGlass" ++ ExposureEventSecurityTest ExposureEventType = "X-SecurityTest" ++ ExposureEventIncident ExposureEventType = "X-Incident" ++ ExposureEventLegalDemand ExposureEventType = "X-LegalDemand" ++ ExposureEventComplianceAudit ExposureEventType = "X-ComplianceAudit" ++ ExposureEventMigration ExposureEventType = "X-Migration" ++ ExposureEventRecovery ExposureEventType = "X-Recovery" ++ ExposureEventAdversarial ExposureEventType = "X-Adversarial" ++ ExposureEventMisconfiguration ExposureEventType = "X-Misconfiguration" ++ ExposureEventInducedAccess ExposureEventType = "X-InducedAccess" ++ ExposureEventPrivilegeEscalation ExposureEventType = "X-PrivilegeEscalation" ++) ++ ++// AccessPath describes how access is exercised. ++type AccessPath string ++ ++const ( ++ AccessPathDirect AccessPath = "direct" ++ AccessPathDelegated AccessPath = "delegated" ++ AccessPathMediated AccessPath = "mediated" ++ AccessPathInduced AccessPath = "induced" ++) ++ ++// CaringScope identifies where a CARING descriptor applies. ++type CaringScope struct { ++ Level ScopeLevel `json:"level" yaml:"level"` ++ ID string `json:"id" yaml:"id"` ++ Parent string `json:"parent,omitempty" yaml:"parent,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Resource string `json:"resource,omitempty" yaml:"resource,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// CaringAccessDescriptor is the executable flex-auth representation of a ++// CARING access assignment. ++type CaringAccessDescriptor struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Profile string `json:"profile" yaml:"profile"` ++ SubjectType SubjectType `json:"subject_type" yaml:"subject_type"` ++ OrganizationRelation OrganizationRelation `json:"organization_relation" yaml:"organization_relation"` ++ CanonicalRole CanonicalRole `json:"canonical_role" yaml:"canonical_role"` ++ Scope CaringScope `json:"scope" yaml:"scope"` ++ Planes []Plane `json:"planes" yaml:"planes"` ++ Capabilities []Capability `json:"capabilities" yaml:"capabilities"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ LifecycleState LifecycleState `json:"lifecycle_state,omitempty" yaml:"lifecycle_state,omitempty"` ++ Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"` ++ ExposureEvent ExposureEventType `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ AccessPath AccessPath `json:"access_path,omitempty" yaml:"access_path,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// CaringDerivedCapability records effective authority created by another grant. ++type CaringDerivedCapability struct { ++ Capability Capability `json:"capability" yaml:"capability"` ++ Reason string `json:"reason" yaml:"reason"` ++ Source string `json:"source,omitempty" yaml:"source,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++} ++ ++// CaringConformanceFinding is a diagnostic emitted by descriptive or ++// prescriptive CARING validation. ++type CaringConformanceFinding struct { ++ Code string `json:"code" yaml:"code"` ++ Severity string `json:"severity" yaml:"severity"` ++ Message string `json:"message" yaml:"message"` ++ Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"` ++ Descriptor string `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// CaringExposureEvent records exceptional or irregular information exposure. ++type CaringExposureEvent struct { ++ ID string `json:"id" yaml:"id"` ++ Type ExposureEventType `json:"type" yaml:"type"` ++ Actor string `json:"actor" yaml:"actor"` ++ Subject string `json:"subject" yaml:"subject"` ++ Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ Scope *CaringScope `json:"scope,omitempty" yaml:"scope,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ CapabilitiesUsed []Capability `json:"capabilities_used,omitempty" yaml:"capabilities_used,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Reason string `json:"reason" yaml:"reason"` ++ AuthoritySource string `json:"authority_source,omitempty" yaml:"authority_source,omitempty"` ++ Approval string `json:"approval,omitempty" yaml:"approval,omitempty"` ++ StartTime string `json:"start_time,omitempty" yaml:"start_time,omitempty"` ++ EndTime string `json:"end_time,omitempty" yaml:"end_time,omitempty"` ++ ResourcesAccessed []string `json:"resources_accessed,omitempty" yaml:"resources_accessed,omitempty"` ++ Evidence []string `json:"evidence,omitempty" yaml:"evidence,omitempty"` ++ NotificationStatus string `json:"notification_status,omitempty" yaml:"notification_status,omitempty"` ++ PostReview string `json:"post_review,omitempty" yaml:"post_review,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/resource_manifest.go +@@ -0,0 +1,32 @@ ++package api ++ ++// ResourceManifest is the shape a protected system publishes to register ++// its resources with flex-auth. The shape is pinned against the ++// Markitect-side emitter in markitect-tool (MKTT-WP-0014); see ++// schemas/resource_manifest.schema.json for the JSON Schema and ++// examples/markitect/resource_manifest.yaml for the canonical example. ++type ResourceManifest struct { ++ ID string `json:"id" yaml:"id"` ++ System string `json:"system" yaml:"system"` ++ Resources []Resource `json:"resources" yaml:"resources"` ++ Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"` ++ CaringProfile string `json:"caring_profile,omitempty" yaml:"caring_profile,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Resource is one entry in a ResourceManifest. ++type Resource struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type" yaml:"type"` ++ Path string `json:"path,omitempty" yaml:"path,omitempty"` ++ Parent string `json:"parent,omitempty" yaml:"parent,omitempty"` ++ Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"` ++ TrustZone string `json:"trust_zone,omitempty" yaml:"trust_zone,omitempty"` ++ Owner string `json:"owner,omitempty" yaml:"owner,omitempty"` ++ Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// FlexAuthContractV0 is the metadata.flex_auth_contract value that ++// signals the v0 resource-registration contract. ++const FlexAuthContractV0 = "resource-registration-v0" +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/decisionpep/client.go +@@ -0,0 +1,296 @@ ++// Package decisionpep authenticates the producer's decision record for one ++// current request. It never evaluates policy, caches a verdict, or discovers trust. ++package decisionpep ++ ++import ( ++ "bytes" ++ "context" ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/tls" ++ "crypto/x509" ++ "encoding/base64" ++ "encoding/hex" ++ "encoding/json" ++ "errors" ++ "io" ++ "net/http" ++ "net/url" ++ "os" ++ "path/filepath" ++ "strings" ++ "time" ++ ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++var ErrRefused = errors.New("authenticated decision refused") ++ ++const nonceKey = "decisionpep_request_nonce" ++ ++const JSONProfile = "lossless-json-v1" ++const provenanceMode = "standalone/lossless-json-v1" ++const maxBytes = 2 << 20 ++ ++// Admission is public, independently admitted owner configuration. TokenFile is ++// only a path to a private rotating caller credential, never a literal token. ++type Admission struct { ++ Endpoint string `json:"endpoint"` ++ CAFile string `json:"ca_file"` ++ TokenFile string `json:"token_file"` ++ Keys map[string]string `json:"keys"` ++ Evaluator string `json:"evaluator"` ++ PolicyPackage string `json:"policy_package"` ++ PolicyVersion string `json:"policy_version"` ++ PolicyDigest string `json:"policy_digest"` ++ RegistryDigest string `json:"registry_digest"` ++ CallerPrincipal string `json:"caller_principal"` ++ CallerAudience string `json:"caller_audience"` ++ MaxTTL string `json:"max_ttl"` ++ MaxClockSkew string `json:"max_clock_skew"` ++} ++ ++type Client struct { ++ admission Admission ++ keys map[string]ed25519.PublicKey ++ http *http.Client ++ maxTTL, skew time.Duration ++ now func() time.Time ++} ++ ++// Load reads a current independently admitted file. No trust is bootstrapped ++// from a PDP response, token contents, discovery endpoint, or environment value. ++func Load(path string) (*Client, error) { ++ raw, err := readFile(path, false) ++ if err != nil { ++ return nil, ErrRefused ++ } ++ var a Admission ++ d := json.NewDecoder(bytes.NewReader(raw)) ++ d.DisallowUnknownFields() ++ if d.Decode(&a) != nil { ++ return nil, ErrRefused ++ } ++ if _, err = d.Token(); err != io.EOF { ++ return nil, ErrRefused ++ } ++ return New(a) ++} ++ ++func New(a Admission) (*Client, error) { ++ u, err := url.Parse(a.Endpoint) ++ if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Path != "/v1/check" || u.RawQuery != "" || u.Fragment != "" { ++ return nil, ErrRefused ++ } ++ if !filepath.IsAbs(a.CAFile) || !filepath.IsAbs(a.TokenFile) || a.Evaluator == "" || a.PolicyPackage == "" || a.PolicyVersion == "" || a.CallerPrincipal == "" || a.CallerAudience == "" { ++ return nil, ErrRefused ++ } ++ if !validDigest(a.PolicyDigest) || !validDigest(a.RegistryDigest) || len(a.Keys) == 0 { ++ return nil, ErrRefused ++ } ++ ttl, err := time.ParseDuration(a.MaxTTL) ++ if err != nil || ttl <= 0 { ++ return nil, ErrRefused ++ } ++ skew, err := time.ParseDuration(a.MaxClockSkew) ++ if err != nil || skew < 0 || skew > ttl { ++ return nil, ErrRefused ++ } ++ ca, err := readFile(a.CAFile, false) ++ if err != nil { ++ return nil, ErrRefused ++ } ++ roots := x509.NewCertPool() ++ if !roots.AppendCertsFromPEM(ca) { ++ return nil, ErrRefused ++ } ++ keys := map[string]ed25519.PublicKey{} ++ for kid, value := range a.Keys { ++ b, e := base64.RawURLEncoding.Strict().DecodeString(value) ++ if e != nil || kid == "" || len(b) != ed25519.PublicKeySize { ++ return nil, ErrRefused ++ } ++ keys[kid] = append(ed25519.PublicKey(nil), b...) ++ } ++ transport := &http.Transport{Proxy: nil, TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}, DisableKeepAlives: true} ++ client := &http.Client{Transport: transport, Timeout: 10 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return ErrRefused }} ++ return &Client{admission: a, keys: keys, http: client, maxTTL: ttl, skew: skew, now: time.Now}, nil ++} ++ ++func validDigest(value string) bool { ++ if !strings.HasPrefix(value, "sha256:") || len(value) != 71 { ++ return false ++ } ++ _, e := hex.DecodeString(value[7:]) ++ return e == nil && strings.ToLower(value) == value ++} ++ ++func readFile(path string, private bool) ([]byte, error) { ++ if !filepath.IsAbs(path) { ++ return nil, ErrRefused ++ } ++ f, e := os.Open(path) ++ if e != nil { ++ return nil, ErrRefused ++ } ++ defer f.Close() ++ info, e := f.Stat() ++ if e != nil || !info.Mode().IsRegular() || info.Mode().Perm()&0022 != 0 { ++ return nil, ErrRefused ++ } ++ if private && info.Mode().Perm()&0007 != 0 { ++ return nil, ErrRefused ++ } ++ b, e := io.ReadAll(io.LimitReader(f, maxBytes+1)) ++ if e != nil || len(b) > maxBytes { ++ return nil, ErrRefused ++ } ++ return b, nil ++} ++ ++// Check sends exactly one request with a cryptographically fresh nonce. The ++// returned envelope is the producer's authenticated typed interpretation. ++// Callers must execute only the request's exact operation and honor their own ++// execution boundary; a login decision is never a later resource decision. ++func (c *Client) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) { ++ refuse := func() (api.DecisionEnvelope, error) { return api.DecisionEnvelope{}, ErrRefused } ++ if c == nil || request.Purpose != "" || request.Tenant == "" || request.Subject.ID == "" || request.Subject.Type == "" || request.Resource.ID == "" || request.Resource.Type == "" || request.Resource.System == "" || request.Action == "" { ++ return refuse() ++ } ++ if request.Subject.Tenant != request.Tenant || request.Resource.Tenant != request.Tenant { ++ return refuse() ++ } ++ if request.PolicyVersion != "" && request.PolicyVersion != c.admission.PolicyVersion { ++ return refuse() ++ } ++ // Use the producer's typed representation and preserve numeric lexemes. A ++ // producer that rounds before hashing fails this join rather than weakening it. ++ raw, err := json.Marshal(request) ++ if err != nil { ++ return refuse() ++ } ++ decoder := json.NewDecoder(bytes.NewReader(raw)) ++ decoder.UseNumber() ++ if decoder.Decode(&request) != nil { ++ return refuse() ++ } ++ if request.Context == nil { ++ request.Context = map[string]any{} ++ } ++ if _, exists := request.Context[nonceKey]; exists { ++ return refuse() ++ } ++ nonce := make([]byte, 32) ++ if _, err = rand.Read(nonce); err != nil { ++ return refuse() ++ } ++ request.Context[nonceKey] = hex.EncodeToString(nonce) ++ request.Context["decisionpep_json_profile"] = JSONProfile ++ request.ID = hex.EncodeToString(nonce) ++ request.PolicyVersion = c.admission.PolicyVersion ++ raw, err = json.Marshal(request) ++ if err != nil || len(raw) > maxBytes { ++ return refuse() ++ } ++ token, err := readFile(c.admission.TokenFile, true) ++ if err != nil { ++ return refuse() ++ } ++ value := strings.TrimSpace(string(token)) ++ if value == "" || strings.ContainsAny(value, " \t\r\n") { ++ return refuse() ++ } ++ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.admission.Endpoint, bytes.NewReader(raw)) ++ if err != nil { ++ return refuse() ++ } ++ req.Header.Set("Content-Type", "application/json") ++ req.Header.Set("Access-Engine-JSON-Profile", JSONProfile) ++ req.Header.Set("Authorization", "Bearer "+value) ++ started := c.now().UTC() ++ response, err := c.http.Do(req) ++ if err != nil { ++ return refuse() ++ } ++ defer response.Body.Close() ++ if response.StatusCode != http.StatusOK { ++ return refuse() ++ } ++ body, err := io.ReadAll(io.LimitReader(response.Body, maxBytes+1)) ++ if err != nil || len(body) > maxBytes { ++ return refuse() ++ } ++ envelope, err := sign.DecodeEnvelope(body) ++ if err != nil || sign.Verify(envelope, c.keys) != nil { ++ return refuse() ++ } ++ if c.validate(envelope, request, started, c.now().UTC()) != nil { ++ return refuse() ++ } ++ return envelope, nil ++} ++ ++func (c *Client) validate(e api.DecisionEnvelope, r api.CheckRequest, started, now time.Time) error { ++ p := e.Provenance ++ b := e.Binding ++ l := e.Lifetime ++ if e.ID == "" || e.ContractVersion != api.DecisionRecordContractV1 || e.Effect != api.DecisionEffectAllow || e.RequestID != r.ID || len(e.Obligations) != 0 || b == nil || l == nil { ++ return ErrRefused ++ } ++ if p.Evaluator != c.admission.Evaluator || p.Mode != provenanceMode || p.PolicyPackage != c.admission.PolicyPackage || p.PolicyVersion != c.admission.PolicyVersion || p.PolicyPackageDigest != c.admission.PolicyDigest || p.RegistrySnapshotDigest != c.admission.RegistryDigest || e.MatchedPolicyVersion != p.PolicyVersion { ++ return ErrRefused ++ } ++ if b.Purpose != "" || b.SubmittedRequestDigest != api.RequestDigest(r) || b.Tenant != r.Tenant || b.Action != r.Action || api.CanonicalDigest(b.Context) != api.CanonicalDigest(r.Context) { ++ return ErrRefused ++ } ++ if !sameSubject(b.Subject, r.Subject) || !sameResource(b.Resource, r.Resource) || !sameSubject(e.Subject, b.Subject) || !sameResource(e.Resource, b.Resource) { ++ return ErrRefused ++ } ++ evaluated := api.CheckRequest{Tenant: b.Tenant, Subject: b.Subject, Action: b.Action, Resource: b.Resource, Context: b.Context, Purpose: b.Purpose} ++ if b.RequestDigest != api.RequestDigest(evaluated) { ++ return ErrRefused ++ } ++ for k, v := range api.InputClaimDigests(r) { ++ if p.InputClaimDigests[k] != v { ++ return ErrRefused ++ } ++ } ++ caller := p.Caller ++ if caller == nil || caller.Mode != "enforce" || caller.Principal != c.admission.CallerPrincipal || caller.Audience != c.admission.CallerAudience { ++ return ErrRefused ++ } ++ callerEnd, err := time.Parse(time.RFC3339Nano, caller.NotAfter) ++ if err != nil || !now.Before(callerEnd) { ++ return ErrRefused ++ } ++ decision, err := time.Parse(time.RFC3339Nano, p.DecisionTime) ++ if err != nil || decision.Before(started.Add(-c.skew)) || decision.After(now.Add(c.skew)) { ++ return ErrRefused ++ } ++ begin, err := time.Parse(time.RFC3339Nano, l.NotBefore) ++ if err != nil || begin.After(now) { ++ return ErrRefused ++ } ++ end, err := time.Parse(time.RFC3339Nano, l.ExpiresAt) ++ if err != nil || !now.Before(end) || !end.After(begin) || end.Sub(begin) > c.maxTTL || end.After(callerEnd) { ++ return ErrRefused ++ } ++ ttl, err := time.ParseDuration(l.TTL) ++ if err != nil || l.Kind != api.DecisionLifetimeTTL || ttl <= 0 || ttl > c.maxTTL { ++ return ErrRefused ++ } ++ // Producer CompleteDecision uses its exact decision instant for both bounds. ++ // RFC3339 output can truncate subsecond precision, so do not invent lost time. ++ if !begin.Equal(decision) || end.Sub(begin) != ttl { ++ return ErrRefused ++ } ++ return nil ++} ++ ++func sameSubject(a, b api.SubjectRef) bool { ++ return a.ID == b.ID && a.Type == b.Type && a.Tenant == b.Tenant ++} ++func sameResource(a, b api.ResourceRef) bool { ++ return a.ID == b.ID && a.Type == b.Type && a.System == b.System && a.Tenant == b.Tenant ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/decisionpep/client_test.go +@@ -0,0 +1,211 @@ ++package decisionpep ++ ++import ( ++ "context" ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/x509" ++ "crypto/x509/pkix" ++ "encoding/base64" ++ "encoding/json" ++ "encoding/pem" ++ "io" ++ "math/big" ++ "net/http" ++ "os" ++ "path/filepath" ++ "strings" ++ "testing" ++ "time" ++ ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++type transportFunc func(*http.Request) (*http.Response, error) ++ ++func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } ++func testClient(t *testing.T) (*Client, ed25519.PrivateKey, time.Time) { ++ t.Helper() ++ pub, key, e := ed25519.GenerateKey(rand.Reader) ++ if e != nil { ++ t.Fatal(e) ++ } ++ now := time.Date(2026, 10, 18, 1, 2, 3, 0, time.UTC) ++ dir := t.TempDir() ++ cert, e := x509.CreateCertificate(rand.Reader, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, pub, key) ++ if e != nil { ++ t.Fatal(e) ++ } ++ ca := filepath.Join(dir, "ca.pem") ++ token := filepath.Join(dir, "token") ++ if e = os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0600); e != nil { ++ t.Fatal(e) ++ } ++ if e = os.WriteFile(token, []byte("synthetic-caller-token"), 0600); e != nil { ++ t.Fatal(e) ++ } ++ a := Admission{Endpoint: "https://pdp.invalid/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"test-key": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:test:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "1s"} ++ c, e := New(a) ++ if e != nil { ++ t.Fatal(e) ++ } ++ c.now = func() time.Time { return now } ++ return c, key, now ++} ++func testRequest() api.CheckRequest { ++ return api.CheckRequest{Tenant: "tenant:test", Subject: api.SubjectRef{ID: "subject:test", Type: api.SubjectType("Human"), Tenant: "tenant:test", Attributes: map[string]any{"groups": []string{"engineers"}}}, Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: "tenant:test"}, Context: map[string]any{"big": json.Number("9007199254740993"), "fraction": json.Number("0.123456789123456789")}} ++} ++func envelope(c *Client, r api.CheckRequest, key ed25519.PrivateKey, now time.Time) api.DecisionEnvelope { ++ e := api.DecisionEnvelope{ID: "test-decision", RequestID: r.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: c.admission.PolicyVersion, Subject: r.Subject, Resource: r.Resource, Binding: api.NewDecisionBindingFor(r, r), Provenance: api.DecisionProvenance{Evaluator: c.admission.Evaluator, Mode: provenanceMode, PolicyPackage: c.admission.PolicyPackage, PolicyVersion: c.admission.PolicyVersion, PolicyPackageDigest: c.admission.PolicyDigest, RegistrySnapshotDigest: c.admission.RegistryDigest, Caller: &api.CallerProvenance{Mode: "enforce", Principal: c.admission.CallerPrincipal, Audience: c.admission.CallerAudience, NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}} ++ api.CompleteDecision(&e, r, api.DecisionCompletion{AllowTTL: "10s", Now: now}) ++ if sign.Sign(&e, key, "test-key") != nil { ++ panic("test sign") ++ } ++ return e ++} ++func respond(t *testing.T, c *Client, key ed25519.PrivateKey, now time.Time, mutate func(*api.DecisionEnvelope), resign bool) { ++ t.Helper() ++ c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) { ++ if req.Header.Get("Access-Engine-JSON-Profile") != JSONProfile { ++ t.Fatal("missing lossless profile") ++ } ++ if req.Header.Get("Authorization") != "Bearer synthetic-caller-token" { ++ t.Fatal("wrong synthetic caller token") ++ } ++ var r api.CheckRequest ++ d := json.NewDecoder(req.Body) ++ d.UseNumber() ++ if d.Decode(&r) != nil { ++ t.Fatal("request decode") ++ } ++ if r.Context["big"] != json.Number("9007199254740993") || r.Context["fraction"] != json.Number("0.123456789123456789") { ++ t.Fatal("request numeric precision lost") ++ } ++ if len(r.Context[nonceKey].(string)) != 64 || r.ID != r.Context[nonceKey] { ++ t.Fatal("nonce") ++ } ++ e := envelope(c, r, key, now) ++ if mutate != nil { ++ mutate(&e) ++ } ++ if resign { ++ if sign.Sign(&e, key, "test-key") != nil { ++ t.Fatal("sign") ++ } ++ } ++ b, err := json.Marshal(e) ++ if err != nil { ++ t.Fatal(err) ++ } ++ return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(b))), Header: http.Header{}}, nil ++ })} ++} ++func TestAuthenticDecisionProducerCanonicalPrecision(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, nil, false) ++ e, err := c.Check(context.Background(), testRequest()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if e.Binding.Context["big"] != json.Number("9007199254740993") || e.Binding.Context["fraction"] != json.Number("0.123456789123456789") { ++ t.Fatal("signed typed precision lost") ++ } ++ if sign.Verify(e, c.keys) != nil { ++ t.Fatal("returned envelope not authentic producer bytes") ++ } ++} ++func TestDecisionAdverseBindings(t *testing.T) { ++ cases := map[string]func(*api.DecisionEnvelope){ ++ "unsigned": func(e *api.DecisionEnvelope) { e.Signature = api.UnsignedEnvelopeSignature() }, ++ "effect": func(e *api.DecisionEnvelope) { e.Effect = api.DecisionEffectDeny }, ++ "evaluator": func(e *api.DecisionEnvelope) { e.Provenance.Evaluator = "impostor" }, ++ "policy": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackage = "other" }, ++ "version": func(e *api.DecisionEnvelope) { e.Provenance.PolicyVersion = "v2" }, ++ "policy-digest": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64) }, ++ "registry": func(e *api.DecisionEnvelope) { ++ e.Provenance.RegistrySnapshotDigest = "sha256:" + strings.Repeat("4", 64) ++ }, ++ "tenant": func(e *api.DecisionEnvelope) { e.Binding.Tenant = "other" }, ++ "actor": func(e *api.DecisionEnvelope) { e.Binding.Subject.ID = "other" }, ++ "action": func(e *api.DecisionEnvelope) { e.Binding.Action = "write" }, ++ "resource": func(e *api.DecisionEnvelope) { e.Binding.Resource.ID = "other" }, ++ "purpose": func(e *api.DecisionEnvelope) { e.Binding.Purpose = "readback" }, ++ "submitted": func(e *api.DecisionEnvelope) { e.Binding.SubmittedRequestDigest = "wrong" }, ++ "context": func(e *api.DecisionEnvelope) { e.Binding.Context["fraction"] = json.Number("0.123456789123456788") }, ++ "legacy-numeric-profile": func(e *api.DecisionEnvelope) { e.Provenance.Mode = "standalone" }, ++ "caller-warn": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Mode = "warn" }, ++ "caller-principal": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Principal = "other" }, ++ "caller-audience": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Audience = "other" }, ++ "caller-expired": func(e *api.DecisionEnvelope) { e.Provenance.Caller.NotAfter = "2020-01-01T00:00:00Z" }, ++ "stale-decision": func(e *api.DecisionEnvelope) { e.Provenance.DecisionTime = "2020-01-01T00:00:00Z" }, ++ "expired": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2020-01-01T00:00:00Z" }, ++ "future-begin": func(e *api.DecisionEnvelope) { e.Lifetime.NotBefore = "2030-01-01T00:00:00Z" }, ++ "subnanosecond-expiry": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2026-10-18T01:02:02.999999999Z" }, ++ "missing-TTL": func(e *api.DecisionEnvelope) { e.Lifetime = nil }, ++ "excess-TTL": func(e *api.DecisionEnvelope) { e.Lifetime.TTL = "1h" }, ++ "obligation": func(e *api.DecisionEnvelope) { e.Obligations = []api.Obligation{{Type: "unimplemented"}} }, ++ } ++ for name, mutation := range cases { ++ t.Run(name, func(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, mutation, name != "unsigned") ++ if _, err := c.Check(context.Background(), testRequest()); err == nil { ++ t.Fatal("accepted wrong signed binding") ++ } ++ }) ++ } ++} ++func TestSignatureForgeryAndReplay(t *testing.T) { ++ t.Run("forged-payload", func(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, func(e *api.DecisionEnvelope) { e.Reason = "changed after signature" }, false) ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("forgery accepted") ++ } ++ }) ++ t.Run("wrong-key", func(t *testing.T) { ++ c, _, now := testClient(t) ++ _, key, _ := ed25519.GenerateKey(rand.Reader) ++ respond(t, c, key, now, nil, false) ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("wrong key accepted") ++ } ++ }) ++ t.Run("replay", func(t *testing.T) { ++ c, key, now := testClient(t) ++ var saved []byte ++ c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) { ++ if saved == nil { ++ var r api.CheckRequest ++ d := json.NewDecoder(req.Body) ++ d.UseNumber() ++ if d.Decode(&r) != nil { ++ t.Fatal("decode") ++ } ++ e := envelope(c, r, key, now) ++ saved, _ = json.Marshal(e) ++ } ++ return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(saved)))}, nil ++ })} ++ if _, e := c.Check(context.Background(), testRequest()); e != nil { ++ t.Fatal(e) ++ } ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("old decision replay accepted for new nonce") ++ } ++ }) ++} ++func TestUnavailableAndPublicTokenFileRefuse(t *testing.T) { ++ c, _, _ := testClient(t) ++ c.http = &http.Client{Transport: transportFunc(func(*http.Request) (*http.Response, error) { return nil, io.ErrUnexpectedEOF })} ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("unavailable allowed") ++ } ++ if e := os.Chmod(c.admission.TokenFile, 0644); e != nil { ++ t.Fatal(e) ++ } ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("public token file allowed") ++ } ++} +--- /dev/null ++++ b/third_party/access-engine-pep/producer-source.json +@@ -0,0 +1,30 @@ ++{ ++ "upstream": "github.com/netkingdom/access-engine", ++ "commit": "d328a2601095686f74376d87791bff01e2a3a2a3", ++ "exact_unchanged_files": { ++ "pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19", ++ "pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3", ++ "pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f", ++ "internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde", ++ "LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6" ++ }, ++ "module": "Standalone Go1.24 source slice with original module/import identity; no canonical/schema/signature modifications", ++ "new_code": [ ++ "pkg/decisionpep/client.go" ++ ], ++ "fixture_code": [ ++ "testsupport/signfixture/sign.go" ++ ], ++ "source_of_truth": "repository root; generated with tools/export_decisionpep_adapter.py", ++ "exported_file_sha256": { ++ "LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6", ++ "go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3", ++ "internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde", ++ "pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19", ++ "pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3", ++ "pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f", ++ "pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba", ++ "pkg/decisionpep/client_test.go": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62", ++ "testsupport/signfixture/sign.go": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29" ++ } ++} +--- /dev/null ++++ b/third_party/access-engine-pep/testsupport/signfixture/sign.go +@@ -0,0 +1,14 @@ ++// Package signfixture exposes producer signing only for synthetic consumer tests. ++// Callers supply test keys; this package contains no credentials or runtime signer. ++package signfixture ++ ++import ( ++ "crypto/ed25519" ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++// Sign uses the unchanged producer envelope format with a caller-owned fixture key. ++func Sign(envelope *api.DecisionEnvelope, key ed25519.PrivateKey, kid string) error { ++ return sign.Sign(envelope, key, kid) ++} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-patch-verification.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-patch-verification.json new file mode 100644 index 0000000..4961fff --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-patch-verification.json @@ -0,0 +1,7 @@ +{ + "paths_verified": 40, + "all_postimages_exact": true, + "patch_sha256": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c", + "workspace": "/tmp/custodian-forgejo-pg-composite-k93fv80k", + "native_changes": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-source-inventory.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-source-inventory.json new file mode 100644 index 0000000..450af02 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/composed-source-inventory.json @@ -0,0 +1,219 @@ +{ + "schema": "custodian.forgejo-composed-source.v1", + "status": "source-tested PostgreSQL/context correction; complete release rebuild pending", + "archive_sha256": "1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c", + "forgejo_version": "11.0.3", + "producer_source_commit": "ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3", + "issuer_source_commit": "git:d803e9282a47b49daf930fb905312fa5104c0ace", + "patch_sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c", + "files": [ + { + "path": "cmd/admin_auth_oauth.go", + "before_sha256": "d5faee30ffbb9a560256c92a0e05e652b79e95e016448cbac7dbb6ad0fdad937", + "after_sha256": "d718d817c46f6393488a9c5a894b7535640cd0ec1a1ae3f72f8411a2bbec3b38" + }, + { + "path": "cmd/admin_auth_oauth_secret_linux.go", + "before_sha256": null, + "after_sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b" + }, + { + "path": "cmd/admin_auth_oauth_secret_linux_test.go", + "before_sha256": null, + "after_sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2" + }, + { + "path": "cmd/admin_auth_oauth_secret_other.go", + "before_sha256": null, + "after_sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d" + }, + { + "path": "go.mod", + "before_sha256": "652162a4a95c6d1c349d2a8c0b514def5abd837f001137b77b7b3f4a0f162d00", + "after_sha256": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a" + }, + { + "path": "models/auth/source_operation.go", + "before_sha256": null, + "after_sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31" + }, + { + "path": "models/auth/source_operation_internal_test.go", + "before_sha256": null, + "after_sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb" + }, + { + "path": "models/auth/source_operation_postgres_export_test.go", + "before_sha256": null, + "after_sha256": "b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d" + }, + { + "path": "models/auth/source_operation_postgres_test.go", + "before_sha256": null, + "after_sha256": "4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe" + }, + { + "path": "models/auth/source_operation_process_test.go", + "before_sha256": null, + "after_sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd" + }, + { + "path": "models/auth/source_operation_test.go", + "before_sha256": null, + "after_sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428" + }, + { + "path": "models/db/context.go", + "before_sha256": "10774e272206c7a8d6ebfb106b37d535de77f7e96ba57657b4dad960639f8519", + "after_sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7" + }, + { + "path": "models/db/engine.go", + "before_sha256": "774cedc74e1a8d9ae6f9e5f106c6cbb66f12c11b42e99ef07a7affd03269c3d2", + "after_sha256": "d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8" + }, + { + "path": "models/db/sensitive_sql.go", + "before_sha256": null, + "after_sha256": "06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584" + }, + { + "path": "models/db/sensitive_sql_logging_test.go", + "before_sha256": null, + "after_sha256": "d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7" + }, + { + "path": "models/db/sensitive_sql_test.go", + "before_sha256": null, + "after_sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b" + }, + { + "path": "models/db/tracing_context_test.go", + "before_sha256": null, + "after_sha256": "612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + { + "path": "routers/web/admin/auths.go", + "before_sha256": "eeed6999e4655993b55873bb51efb4fcd946532255b9473d16e086e04a1ec911", + "after_sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923" + }, + { + "path": "routers/web/auth/oauth.go", + "before_sha256": "6f454b3550b150178a8597a9437adb0c626556f34c15a13778c15d675b05659b", + "after_sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17" + }, + { + "path": "services/auth/source/oauth2/access_engine.go", + "before_sha256": null, + "after_sha256": "d5f07f9491ac2212410e1fa8c0248d68ba3a723e7ca99e363627c5292d187aa8" + }, + { + "path": "services/auth/source/oauth2/access_engine_assurance.go", + "before_sha256": null, + "after_sha256": "42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642" + }, + { + "path": "services/auth/source/oauth2/access_engine_assurance_test.go", + "before_sha256": null, + "after_sha256": "44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3" + }, + { + "path": "services/auth/source/oauth2/access_engine_azp_test.go", + "before_sha256": null, + "after_sha256": "5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce" + }, + { + "path": "services/auth/source/oauth2/access_engine_callback_test.go", + "before_sha256": null, + "after_sha256": "5cea36b630c895a9473dc876b7128526ddef65d9395b2fddc56343e162452472" + }, + { + "path": "services/auth/source/oauth2/access_engine_test.go", + "before_sha256": null, + "after_sha256": "99edd6cfe3e834d73e0447a7915f52cc4ab7b82644669c5ae61963f850b447e8" + }, + { + "path": "services/auth/source/oauth2/providers_openid.go", + "before_sha256": "6ddca7fb4ab8b16e6b096481cfb40ca5abdf3a327f38c895b95b3846cc7c2248", + "after_sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf" + }, + { + "path": "services/auth/source/oauth2/required_claim_values.go", + "before_sha256": null, + "after_sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda" + }, + { + "path": "services/auth/source/oauth2/required_claim_values_test.go", + "before_sha256": null, + "after_sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0" + }, + { + "path": "services/auth/source/oauth2/source.go", + "before_sha256": "a6ee5146c07d8efb1837ef91f1ab5f3139f69a1ff66f814d5e9c6053bf9b0d8b", + "after_sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642" + }, + { + "path": "services/auth/source/oauth2/source_callout.go", + "before_sha256": "c30c418182d4cb98b1e448f621c93322accfbbf92d6a59e6df1ae45a7aa8fc7b", + "after_sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c" + }, + { + "path": "third_party/access-engine-pep/LICENSE", + "before_sha256": null, + "after_sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6" + }, + { + "path": "third_party/access-engine-pep/go.mod", + "before_sha256": null, + "after_sha256": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3" + }, + { + "path": "third_party/access-engine-pep/internal/sign/sign.go", + "before_sha256": null, + "after_sha256": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde" + }, + { + "path": "third_party/access-engine-pep/pkg/api/canonical.go", + "before_sha256": null, + "after_sha256": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19" + }, + { + "path": "third_party/access-engine-pep/pkg/api/caring.go", + "before_sha256": null, + "after_sha256": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3" + }, + { + "path": "third_party/access-engine-pep/pkg/api/resource_manifest.go", + "before_sha256": null, + "after_sha256": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f" + }, + { + "path": "third_party/access-engine-pep/pkg/decisionpep/client.go", + "before_sha256": null, + "after_sha256": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba" + }, + { + "path": "third_party/access-engine-pep/pkg/decisionpep/client_test.go", + "before_sha256": null, + "after_sha256": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62" + }, + { + "path": "third_party/access-engine-pep/producer-source.json", + "before_sha256": null, + "after_sha256": "c1d83e3d79f9860ffaa61ec19a6b3415e914d3058b11b07cdd05a7d1182cdceb" + }, + { + "path": "third_party/access-engine-pep/testsupport/signfixture/sign.go", + "before_sha256": null, + "after_sha256": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29" + } + ], + "test_only_postgres_tag": "source_operation_postgres", + "remaining_native_work": [ + "Independent owner/source/package/type admission", + "Current authenticated operation/inverse sessions and release-wide writer exclusion", + "Private evidence/retention/custody and actual deployed database acceptance", + "Ongoing HTTP/API/Git/SSH protected paths and native identity/factor journeys" + ], + "native_gates_closed": 0 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/engine.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/engine.go new file mode 100644 index 0000000..2e6e11f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/engine.go @@ -0,0 +1,391 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package db + +import ( + "context" + "database/sql" + "errors" + "fmt" + "io" + "reflect" + "runtime/trace" + "strings" + "time" + + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + + "xorm.io/xorm" + "xorm.io/xorm/contexts" + "xorm.io/xorm/names" + "xorm.io/xorm/schemas" + + _ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver + _ "github.com/lib/pq" // Needed for the Postgresql driver +) + +var ( + x *xorm.Engine + tables []any + initFuncs []func() error +) + +// Engine represents a xorm engine or session. +type Engine interface { + Table(tableNameOrBean any) *xorm.Session + Count(...any) (int64, error) + Decr(column string, arg ...any) *xorm.Session + Delete(...any) (int64, error) + Truncate(...any) (int64, error) + Exec(...any) (sql.Result, error) + Find(any, ...any) error + Get(beans ...any) (bool, error) + ID(any) *xorm.Session + In(string, ...any) *xorm.Session + Incr(column string, arg ...any) *xorm.Session + Insert(...any) (int64, error) + Iterate(any, xorm.IterFunc) error + IsTableExist(any) (bool, error) + Join(joinOperator string, tablename, condition any, args ...any) *xorm.Session + SQL(any, ...any) *xorm.Session + Where(any, ...any) *xorm.Session + Asc(colNames ...string) *xorm.Session + Desc(colNames ...string) *xorm.Session + Limit(limit int, start ...int) *xorm.Session + NoAutoTime() *xorm.Session + SumInt(bean any, columnName string) (res int64, err error) + Sync(...any) error + Select(string) *xorm.Session + SetExpr(string, any) *xorm.Session + NotIn(string, ...any) *xorm.Session + OrderBy(any, ...any) *xorm.Session + Exist(...any) (bool, error) + Distinct(...string) *xorm.Session + Query(...any) ([]map[string][]byte, error) + Cols(...string) *xorm.Session + Context(ctx context.Context) *xorm.Session + Ping() error +} + +// TableInfo returns table's information via an object +func TableInfo(v any) (*schemas.Table, error) { + return x.TableInfo(v) +} + +// DumpTables dump tables information +func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { + return x.DumpTables(tables, w, tp...) +} + +// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync +func RegisterModel(bean any, initFunc ...func() error) { + tables = append(tables, bean) + if len(initFuncs) > 0 && initFunc[0] != nil { + initFuncs = append(initFuncs, initFunc[0]) + } +} + +func init() { + gonicNames := []string{"SSL", "UID"} + for _, name := range gonicNames { + names.LintGonicMapper[name] = true + } +} + +// newXORMEngine returns a new XORM engine from the configuration +func newXORMEngine() (*xorm.Engine, error) { + connStr, err := setting.DBConnStr() + if err != nil { + return nil, err + } + + var engine *xorm.Engine + + if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 { + // OK whilst we sort out our schema issues - create a schema aware postgres + registerPostgresSchemaDriver() + engine, err = xorm.NewEngine("postgresschema", connStr) + } else { + engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr) + } + + if err != nil { + return nil, err + } + if setting.Database.Type.IsMySQL() { + engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) + } + engine.SetSchema(setting.Database.Schema) + return engine, nil +} + +// SyncAllTables sync the schemas of all tables, is required by unit test code +func SyncAllTables() error { + _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{ + WarnIfDatabaseColumnMissed: true, + }, tables...) + return err +} + +// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext +func InitEngine(ctx context.Context) error { + xormEngine, err := newXORMEngine() + if err != nil { + return fmt.Errorf("failed to connect to database: %w", err) + } + + xormEngine.SetMapper(names.GonicMapper{}) + // WARNING: for serv command, MUST remove the output to os.stdout, + // so use log file to instead print to stdout. + xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL)) + xormEngine.ShowSQL(setting.Database.LogSQL) + xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns) + xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns) + xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + xormEngine.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) + xormEngine.SetDefaultContext(ctx) + + if setting.Database.SlowQueryThreshold > 0 { + xormEngine.AddHook(&SlowQueryHook{ + Treshold: setting.Database.SlowQueryThreshold, + Logger: log.GetLogger("xorm"), + }) + } + + errorLogger := log.GetLogger("xorm") + if setting.IsInTesting { + errorLogger = log.GetLogger(log.DEFAULT) + } + + xormEngine.AddHook(&ErrorQueryHook{ + Logger: errorLogger, + }) + + xormEngine.AddHook(&TracingHook{}) + + SetDefaultEngine(ctx, xormEngine) + return nil +} + +// SetDefaultEngine sets the default engine for db +func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) { + x = eng + DefaultContext = &Context{ + Context: ctx, + e: x, + } +} + +// UnsetDefaultEngine closes and unsets the default engine +// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now, +// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close +// Global database engine related functions are all racy and there is no graceful close right now. +func UnsetDefaultEngine() { + if x != nil { + _ = x.Close() + x = nil + } + DefaultContext = nil +} + +// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext +// This function must never call .Sync() if the provided migration function fails. +// When called from the "doctor" command, the migration function is a version check +// that prevents the doctor from fixing anything in the database if the migration level +// is different from the expected value. +func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) { + if err = InitEngine(ctx); err != nil { + return err + } + + if err = x.Ping(); err != nil { + return err + } + + preprocessDatabaseCollation(x) + + // We have to run migrateFunc here in case the user is re-running installation on a previously created DB. + // If we do not then table schemas will be changed and there will be conflicts when the migrations run properly. + // + // Installation should only be being re-run if users want to recover an old database. + // However, we should think carefully about should we support re-install on an installed instance, + // as there may be other problems due to secret reinitialization. + if err = migrateFunc(x); err != nil { + return fmt.Errorf("migrate: %w", err) + } + + if err = SyncAllTables(); err != nil { + return fmt.Errorf("sync database struct error: %w", err) + } + + for _, initFunc := range initFuncs { + if err := initFunc(); err != nil { + return fmt.Errorf("initFunc failed: %w", err) + } + } + + return nil +} + +// NamesToBean return a list of beans or an error +func NamesToBean(names ...string) ([]any, error) { + beans := []any{} + if len(names) == 0 { + beans = append(beans, tables...) + return beans, nil + } + // Need to map provided names to beans... + beanMap := make(map[string]any) + for _, bean := range tables { + beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean + beanMap[strings.ToLower(x.TableName(bean))] = bean + beanMap[strings.ToLower(x.TableName(bean, true))] = bean + } + + gotBean := make(map[any]bool) + for _, name := range names { + bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))] + if !ok { + return nil, fmt.Errorf("no table found that matches: %s", name) + } + if !gotBean[bean] { + beans = append(beans, bean) + gotBean[bean] = true + } + } + return beans, nil +} + +// DumpDatabase dumps all data from database according the special database SQL syntax to file system. +func DumpDatabase(filePath, dbType string) error { + var tbs []*schemas.Table + for _, t := range tables { + t, err := x.TableInfo(t) + if err != nil { + return err + } + tbs = append(tbs, t) + } + + type Version struct { + ID int64 `xorm:"pk autoincr"` + Version int64 + } + t, err := x.TableInfo(&Version{}) + if err != nil { + return err + } + tbs = append(tbs, t) + + if len(dbType) > 0 { + return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType)) + } + return x.DumpTablesToFile(tbs, filePath) +} + +// MaxBatchInsertSize returns the table's max batch insert size +func MaxBatchInsertSize(bean any) int { + t, err := x.TableInfo(bean) + if err != nil { + return 50 + } + return 999 / len(t.ColumnsSeq()) +} + +// IsTableNotEmpty returns true if table has at least one record +func IsTableNotEmpty(beanOrTableName any) (bool, error) { + return x.Table(beanOrTableName).Exist() +} + +// DeleteAllRecords will delete all the records of this table +func DeleteAllRecords(tableName string) error { + _, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName)) + return err +} + +// GetMaxID will return max id of the table +func GetMaxID(beanOrTableName any) (maxID int64, err error) { + _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID) + return maxID, err +} + +func SetLogSQL(ctx context.Context, on bool) { + e := GetEngine(ctx) + if x, ok := e.(*xorm.Engine); ok { + x.ShowSQL(on) + } else if sess, ok := e.(*xorm.Session); ok { + sess.Engine().ShowSQL(on) + } +} + +type TracingHook struct{} + +var _ contexts.Hook = &TracingHook{} + +type sqlTask struct{} + +func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + ctx, task := trace.NewTask(c.Ctx, "sql") + ctx = context.WithValue(ctx, sqlTask{}, task) + // XORM passes the same ContextHook to every BeforeProcess hook. Publish + // the task there too, so a later hook returning c.Ctx retains it. + c.Ctx = ctx + if isSensitiveSQL(c.Ctx) { + trace.Log(ctx, "query", "[owner-confined query]") + } else { + trace.Log(ctx, "query", c.SQL) + trace.Logf(ctx, "args", "%v", c.Args) + } + return ctx, nil +} + +func (TracingHook) AfterProcess(c *contexts.ContextHook) error { + c.Ctx.Value(sqlTask{}).(*trace.Task).End() + return nil +} + +type SlowQueryHook struct { + Treshold time.Duration + Logger log.Logger +} + +var _ contexts.Hook = &SlowQueryHook{} + +func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + return c.Ctx, nil +} + +func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.ExecuteTime >= h.Treshold { + if isSensitiveSQL(c.Ctx) { + h.Logger.Log(8, log.WARN, "[Slow SQL Query] [owner-confined query] - %v", c.ExecuteTime) + } else { + h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) + } + } + return nil +} + +type ErrorQueryHook struct { + Logger log.Logger +} + +var _ contexts.Hook = &ErrorQueryHook{} + +func (ErrorQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + return c.Ctx, nil +} + +func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.Err != nil && !errors.Is(c.Err, context.Canceled) { + if isSensitiveSQL(c.Ctx) { + h.Logger.Log(8, log.ERROR, "[Error SQL Query] [owner-confined query]") + } else { + h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) + } + } + return nil +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/tracing_context_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/tracing_context_test.go new file mode 100644 index 0000000..3bbe315 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/consumer-source/models/db/tracing_context_test.go @@ -0,0 +1,54 @@ +package db + +import ( + "bytes" + "context" + "runtime/trace" + "testing" + + "xorm.io/xorm" + "xorm.io/xorm/contexts" +) + +type followingQueryHook struct{} + +func (followingQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + return c.Ctx, nil +} +func (followingQueryHook) AfterProcess(*contexts.ContextHook) error { return nil } + +func TestTracingContextSurvivesFollowingHooks(t *testing.T) { + for _, sensitive := range []bool{false, true} { + for _, fail := range []bool{false, true} { + engine, err := xorm.NewEngine("sqlite3", ":memory:") + if err != nil { + t.Fatal(err) + } + engine.AddHook(TracingHook{}) + engine.AddHook(followingQueryHook{}) + ctx := context.Background() + if sensitive { + ctx = WithSensitiveSQL(ctx) + } + var output bytes.Buffer + if err := trace.Start(&output); err != nil { + t.Fatal(err) + } + func() { + defer trace.Stop() + query := "SELECT ?" + if fail { + query = "SELECT missing_synthetic_function(?)" + } + _, err = engine.Context(ctx).Exec(query, "synthetic-following-hook-sentinel") + }() + _ = engine.Close() + if (err != nil) != fail { + t.Fatal("unexpected real SQLite query outcome") + } + if bytes.Contains(output.Bytes(), []byte("synthetic-following-hook-sentinel")) == sensitive { + t.Fatal("following hook changed private or ordinary trace disclosure") + } + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/controlled-counterexamples.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/controlled-counterexamples.json new file mode 100644 index 0000000..86ec9f1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/controlled-counterexamples.json @@ -0,0 +1,45 @@ +{ + "controls": [ + { + "receipt": "postgres-suite-attempt5.json", + "mutation": { + "mutation": "omit-postgres-lock", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:137bcc05baf724abfcb9c48a2ef45d5064a6a163c4ba98269364eafb9a1096cd", + "production_source_changed": false + }, + "failed_tests": [ + "TestSourceOperationPostgres/default/uncooperative-writers-excluded", + "TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner", + "TestSourceOperationPostgres/default", + "TestSourceOperationPostgres/public/uncooperative-writers-excluded", + "TestSourceOperationPostgres/public", + "TestSourceOperationPostgres/named/uncooperative-writers-excluded", + "TestSourceOperationPostgres/named", + "TestSourceOperationPostgres" + ], + "all_three_database_profiles_detect_the_removed_control": true + }, + { + "receipt": "postgres-suite-attempt6.json", + "mutation": { + "mutation": "omit-private-context", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:0b9e248b685a950cfbf31f76aafd54e1b99272402a2f05bc6d8b742bc2657d82", + "production_source_changed": false + }, + "failed_tests": [ + "TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics", + "TestSourceOperationPostgres/default", + "TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics", + "TestSourceOperationPostgres/public", + "TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics", + "TestSourceOperationPostgres/named", + "TestSourceOperationPostgres" + ], + "all_three_database_profiles_detect_the_removed_control": true + } + ], + "production_source_changed": false, + "counterexamples_are_not_successful_normal_runs": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.json new file mode 100644 index 0000000..876372c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.json @@ -0,0 +1,17 @@ +{ + "container_state": { + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 2, + "Error": "", + "StartedAt": "2026-10-18T00:52:30.648750486Z", + "FinishedAt": "2026-10-18T00:52:33.100722803Z" + }, + "test_packages_executed": false, + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.log new file mode 100644 index 0000000..2066ae6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-attempt2-failure.log @@ -0,0 +1,45 @@ +The files belonging to this database system will be owned by user "postgres". +This user must also own the server process. + +The database cluster will be initialized with locale "en_US.utf8". +The default database encoding has accordingly been set to "UTF8". +The default text search configuration will be set to "english". + +Data page checksums are disabled. + +fixing permissions on existing directory /var/lib/postgresql/data ... ok +creating subdirectories ... ok +selecting dynamic shared memory implementation ... posix +selecting default max_connections ... 100 +selecting default shared_buffers ... 128MB +selecting default time zone ... UTC +creating configuration files ... ok +running bootstrap script ... ok +performing post-bootstrap initialization ... ok +syncing data to disk ... ok + + +Success. You can now start the database server using: + + pg_ctl -D /var/lib/postgresql/data -l logfile start + +waiting for server to start....2026-10-18 00:52:33.016 UTC [66] LOG: starting PostgreSQL 16.14 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit +2026-10-18 00:52:33.019 UTC [66] LOG: listening on Unix socket "/socket/.s.PGSQL.5432" +2026-10-18 00:52:33.030 UTC [69] LOG: database system was shut down at 2026-10-18 00:52:32 UTC +2026-10-18 00:52:33.039 UTC [66] LOG: database system is ready to accept connections + done +server started + https://www.postgresql.org/docs/current/auth-trust.html + In Docker's default configuration, this is effectively any other + container on the same system. + + It is not recommended to use POSTGRES_HOST_AUTH_METHOD=trust. Replace + it with "-e POSTGRES_PASSWORD=password" instead to set a password in + "docker run". +******************************************************************************** +sh: locale: not found +2026-10-18 00:52:31.773 UTC [48] WARNING: no usable system locales were found +initdb: warning: enabling "trust" authentication for local connections +initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. +psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt2.json new file mode 100644 index 0000000..09386c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt2.json @@ -0,0 +1,7 @@ +{ + "exact_owned_container_removed": true, + "exact_owned_volume_removed": true, + "container_id": "cb87f8327d5572b8a2ebe875db17fbc1b1ecfefa27b63f6282531e152f583005", + "volume": "custodian-forgejo-pg-b8712ab62110-data", + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt3.json new file mode 100644 index 0000000..8a94aca --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup-attempt3.json @@ -0,0 +1,7 @@ +{ + "exact_owned_container_removed": true, + "exact_owned_volume_removed": true, + "container_id": "60dd3cdc9ee384ace5f008fc7d58c1e19eb120cd8585527e7a4752f79d05a659", + "volume": "custodian-forgejo-pg-96ce905f445a-data", + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup.json new file mode 100644 index 0000000..778f72d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-cleanup.json @@ -0,0 +1,7 @@ +{ + "exact_owned_container_removed": true, + "exact_owned_volume_removed": true, + "container_id": "282c533dff17e9d0cbaf5f3ecfdbad4ecbd9b6dc6038c75746a3fcd5880100d9", + "volume": "custodian-forgejo-pg-f1a9a061ff4e-data", + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.json new file mode 100644 index 0000000..7bb3273 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.json @@ -0,0 +1,18 @@ +{ + "container_state": { + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 2, + "Error": "", + "StartedAt": "2026-10-18T00:50:01.120178159Z", + "FinishedAt": "2026-10-18T00:50:07.2845179Z" + }, + "runtime_setup_failed": true, + "test_packages_executed": false, + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.log new file mode 100644 index 0000000..b1acdc1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-initial-failure.log @@ -0,0 +1,35 @@ +The database cluster will be initialized with locale "en_US.utf8". +The default database encoding has accordingly been set to "UTF8". +The default text search configuration will be set to "english". + +Data page checksums are disabled. + +fixing permissions on existing directory /var/lib/postgresql/data ... ok +creating subdirectories ... ok +selecting dynamic shared memory implementation ... posix +selecting default max_connections ... 100 +selecting default shared_buffers ... 128MB +selecting default time zone ... UTC +creating configuration files ... ok +running bootstrap script ... ok +performing post-bootstrap initialization ... ok +syncing data to disk ... ok + + +Success. You can now start the database server using: + + pg_ctl -D /var/lib/postgresql/data -l logfile start + +waiting for server to start....2026-10-18 00:50:07.195 UTC [55] LOG: starting PostgreSQL 16.14 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit +2026-10-18 00:50:07.198 UTC [55] LOG: listening on Unix socket "/socket/.s.PGSQL.5432" +2026-10-18 00:50:07.209 UTC [64] LOG: database system was shut down at 2026-10-18 00:50:06 UTC +2026-10-18 00:50:07.218 UTC [55] LOG: database system is ready to accept connections +2026-10-18 00:50:07.253 UTC [68] FATAL: database "custodian_source" does not exist + done +server started +sh: locale: not found +2026-10-18 00:50:05.943 UTC [42] WARNING: no usable system locales were found +initdb: warning: enabling "trust" authentication for local connections +initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. +psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-restart-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-restart-attempt3.json new file mode 100644 index 0000000..3836a75 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-restart-attempt3.json @@ -0,0 +1,7 @@ +{ + "status": "ready_after_actual_server_restart", + "container_id": "60dd3cdc9ee384ace5f008fc7d58c1e19eb120cd8585527e7a4752f79d05a659", + "retained_volume": "custodian-forgejo-pg-96ce905f445a-data", + "server_version_num": 160014, + "synthetic_data_only": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt2.json new file mode 100644 index 0000000..f77e70b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt2.json @@ -0,0 +1,53 @@ +{ + "trial": "custodian-forgejo-pg-b8712ab62110", + "container_id": "cb87f8327d5572b8a2ebe875db17fbc1b1ecfefa27b63f6282531e152f583005", + "volume": "custodian-forgejo-pg-b8712ab62110-data", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "creation_argv": [ + "docker", + "create", + "--name", + "custodian-forgejo-pg-b8712ab62110", + "--label", + "custodian.source-test=custodian-forgejo-pg-b8712ab62110", + "--network", + "none", + "--memory", + "512m", + "--cpus", + "1", + "--pids-limit", + "128", + "--shm-size", + "64m", + "--mount", + "type=volume,source=custodian-forgejo-pg-b8712ab62110-data,target=/var/lib/postgresql/data", + "--mount", + "type=bind,source=/tmp/custodian-forgejo-postgres-r1-attempt2/socket,target=/socket", + "--env", + "POSTGRES_USER=custodian_test", + "--env", + "POSTGRES_DB=custodian_source", + "--env", + "PGHOST=/socket", + "--env", + "POSTGRES_HOST_AUTH_METHOD=trust", + "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "postgres", + "-c", + "listen_addresses=", + "-c", + "unix_socket_directories=/socket", + "-c", + "unix_socket_permissions=0777", + "-c", + "log_statement=none", + "-c", + "log_min_error_statement=panic" + ], + "synthetic_data_only": true, + "real_credentials": false, + "network": "none", + "ports_published": false, + "authentication": "trust on private synthetic Unix-socket fixture; not a custody/authentication proof" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt3.json new file mode 100644 index 0000000..1383af6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime-attempt3.json @@ -0,0 +1,55 @@ +{ + "trial": "custodian-forgejo-pg-96ce905f445a", + "container_id": "60dd3cdc9ee384ace5f008fc7d58c1e19eb120cd8585527e7a4752f79d05a659", + "volume": "custodian-forgejo-pg-96ce905f445a-data", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "creation_argv": [ + "docker", + "create", + "--name", + "custodian-forgejo-pg-96ce905f445a", + "--label", + "custodian.source-test=custodian-forgejo-pg-96ce905f445a", + "--network", + "none", + "--memory", + "512m", + "--cpus", + "1", + "--pids-limit", + "128", + "--shm-size", + "64m", + "--mount", + "type=volume,source=custodian-forgejo-pg-96ce905f445a-data,target=/var/lib/postgresql/data", + "--mount", + "type=bind,source=/tmp/custodian-forgejo-postgres-r1-attempt3/socket,target=/socket", + "--env", + "POSTGRES_USER=custodian_test", + "--env", + "POSTGRES_DB=custodian_source", + "--env", + "PGHOST=/socket", + "--env", + "POSTGRES_HOST_AUTH_METHOD=trust", + "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "postgres", + "-c", + "listen_addresses=", + "-c", + "unix_socket_directories=/var/run/postgresql,/socket", + "-c", + "unix_socket_permissions=0777", + "-c", + "log_statement=none", + "-c", + "log_min_error_statement=panic" + ], + "synthetic_data_only": true, + "real_credentials": false, + "network": "none", + "ports_published": false, + "authentication": "trust on private synthetic Unix-socket fixture; not a custody/authentication proof", + "server_version_num": 160014, + "status": "ready" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime.json new file mode 100644 index 0000000..6fa9980 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database-runtime.json @@ -0,0 +1,51 @@ +{ + "trial": "custodian-forgejo-pg-f1a9a061ff4e", + "container_id": "282c533dff17e9d0cbaf5f3ecfdbad4ecbd9b6dc6038c75746a3fcd5880100d9", + "volume": "custodian-forgejo-pg-f1a9a061ff4e-data", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "creation_argv": [ + "docker", + "create", + "--name", + "custodian-forgejo-pg-f1a9a061ff4e", + "--label", + "custodian.source-test=custodian-forgejo-pg-f1a9a061ff4e", + "--network", + "none", + "--memory", + "512m", + "--cpus", + "1", + "--pids-limit", + "128", + "--shm-size", + "64m", + "--mount", + "type=volume,source=custodian-forgejo-pg-f1a9a061ff4e-data,target=/var/lib/postgresql/data", + "--mount", + "type=bind,source=/tmp/custodian-forgejo-postgres-r1/socket,target=/socket", + "--env", + "POSTGRES_USER=custodian_test", + "--env", + "POSTGRES_DB=custodian_source", + "--env", + "POSTGRES_HOST_AUTH_METHOD=trust", + "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "postgres", + "-c", + "listen_addresses=", + "-c", + "unix_socket_directories=/socket", + "-c", + "unix_socket_permissions=0777", + "-c", + "log_statement=none", + "-c", + "log_min_error_statement=panic" + ], + "synthetic_data_only": true, + "real_credentials": false, + "network": "none", + "ports_published": false, + "authentication": "trust on private synthetic Unix-socket fixture; not a custody/authentication proof" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database_runtime.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database_runtime.py new file mode 100644 index 0000000..5624dfe --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/database_runtime.py @@ -0,0 +1,121 @@ +"""Own one disposable local PostgreSQL container and its synthetic volume.""" +from pathlib import Path +import argparse +import json +import subprocess +import time +import uuid + +HERE = Path(__file__).resolve().parent +parser = argparse.ArgumentParser() +parser.add_argument('action', choices=['start', 'ready', 'restart', 'cleanup']) +parser.add_argument('--attempt', type=int, default=1) +args = parser.parse_args() +suffix = '' if args.attempt == 1 else '-attempt' + str(args.attempt) +WORK = Path('/tmp/custodian-forgejo-postgres-r1' + suffix) +if args.action == 'start': + WORK.mkdir(exist_ok=True) + (WORK / 'home').mkdir(exist_ok=True) + (WORK / 'docker-config').mkdir(exist_ok=True) +ENV = {'PATH': '/usr/bin:/bin', 'HOME': str(WORK / 'home'), + 'DOCKER_CONFIG': str(WORK / 'docker-config'), 'DOCKER_HOST': 'unix:///var/run/docker.sock'} + +def docker(*arguments): + result = subprocess.run(['docker', *arguments], env=ENV, capture_output=True, + text=True, timeout=90) + assert result.returncode == 0, 'local synthetic database command refused' + return result.stdout.strip() + +def inspect(cid): + return json.loads(docker('inspect', '--format', '{{json .}}', cid)) + +def owned(state): + item = inspect(state['container_id']) + assert item['Config']['Labels']['custodian.source-test'] == state['trial'] + assert item['HostConfig']['NetworkMode'] == 'none' + assert not item['HostConfig']['Privileged'] and not item['HostConfig']['PortBindings'] + assert item['Image'] == state['image_id'] + mounts = item['Mounts'] + assert len(mounts) == 2 + assert any(m['Type'] == 'volume' and m['Name'] == state['volume'] and m['Destination'] == '/var/lib/postgresql/data' for m in mounts) + assert any(m['Type'] == 'bind' and m['Source'] == str(WORK / 'socket') and m['Destination'] == '/socket' for m in mounts) + return item + +def ready(state): + for _ in range(30): + # pg_isready can succeed against the entrypoint's temporary init server, + # before the requested database exists. Require final PID 1 plus a real + # query in that exact synthetic database instead. + process = subprocess.run(['docker', 'exec', state['container_id'], 'cat', '/proc/1/comm'], + env=ENV, capture_output=True, text=True, timeout=5) + result = subprocess.run(['docker', 'exec', state['container_id'], 'psql', + '-h', '/socket', '-U', 'custodian_test', '-d', 'custodian_source', + '-At', '-c', 'SHOW server_version_num'], + env=ENV, capture_output=True, text=True, timeout=5) + if process.returncode == result.returncode == 0 and process.stdout.strip() == 'postgres' and result.stdout.strip().isdecimal(): + break + time.sleep(1) + else: + raise AssertionError('synthetic PostgreSQL did not become ready; retained') + version = result.stdout.strip() + assert version.isdecimal() and int(version) // 10000 == 16 + state['server_version_num'] = int(version) + +state_path = HERE / ('database-runtime' + suffix + '.json') +if args.action == 'start': + assert not state_path.exists() + trial = 'custodian-forgejo-pg-' + uuid.uuid4().hex[:12] + image = json.loads((HERE / 'public-database-image.json').read_text())['image_id'] + volume = trial + '-data' + socket = WORK / 'socket' + socket.mkdir(mode=0o777) + socket.chmod(0o777) # Synthetic Unix socket only, in this new /tmp trial. + docker('volume', 'create', '--label', 'custodian.source-test=' + trial, volume) + argv = ['create', '--name', trial, '--label', 'custodian.source-test=' + trial, + '--network', 'none', '--memory', '512m', '--cpus', '1', '--pids-limit', '128', + '--shm-size', '64m', '--mount', 'type=volume,source=' + volume + ',target=/var/lib/postgresql/data', + '--mount', 'type=bind,source=' + str(socket) + ',target=/socket', + '--env', 'POSTGRES_USER=custodian_test', '--env', 'POSTGRES_DB=custodian_source', + '--env', 'PGHOST=/socket', + '--env', 'POSTGRES_HOST_AUTH_METHOD=trust', image, 'postgres', + '-c', 'listen_addresses=', '-c', 'unix_socket_directories=/var/run/postgresql,/socket', + '-c', 'unix_socket_permissions=0777', '-c', 'log_statement=none', + '-c', 'log_min_error_statement=panic'] + cid = docker(*argv) + state = dict(trial=trial, container_id=cid, volume=volume, image_id=image, + creation_argv=['docker', *argv], synthetic_data_only=True, + real_credentials=False, network='none', ports_published=False, + authentication='trust on private synthetic Unix-socket fixture; not a custody/authentication proof') + state_path.write_text(json.dumps(state, indent=2) + '\n') + owned(state) + docker('start', cid) + ready(state) + state['status'] = 'ready' + state_path.write_text(json.dumps(state, indent=2) + '\n') +else: + state = json.loads(state_path.read_text()) + owned(state) + if args.action == 'ready': + assert inspect(state['container_id'])['State']['Running'] + ready(state) + state['status'] = 'ready' + state_path.write_text(json.dumps(state, indent=2) + '\n') + elif args.action == 'restart': + docker('restart', '--time', '10', state['container_id']) + ready(state) + (HERE / ('database-restart' + suffix + '.json')).write_text(json.dumps(dict( + status='ready_after_actual_server_restart', container_id=state['container_id'], + retained_volume=state['volume'], server_version_num=state['server_version_num'], + synthetic_data_only=True), indent=2) + '\n') + else: + docker('stop', '--time', '10', state['container_id']) + owned(state) + docker('rm', state['container_id']) + volume = json.loads(docker('volume', 'inspect', '--format', '{{json .}}', state['volume'])) + assert volume['Labels']['custodian.source-test'] == state['trial'] + docker('volume', 'rm', state['volume']) + (HERE / ('database-cleanup' + suffix + '.json')).write_text(json.dumps(dict( + exact_owned_container_removed=True, exact_owned_volume_removed=True, + container_id=state['container_id'], volume=state['volume'], + real_credentials=False), indent=2) + '\n') +print(json.dumps({'action': args.action, 'trial': state['trial'], 'synthetic_data_only': True})) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/apply_source.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/apply_source.py new file mode 100644 index 0000000..b5c5965 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/apply_source.py @@ -0,0 +1,48 @@ +"""Apply the tested build candidate, preserving every unrelated owner file.""" +from pathlib import Path +import hashlib +import json +import re +import subprocess + +HERE = Path(__file__).resolve().parent +ROOT = Path('/home/worsch/railiance-apps') +cfg = json.loads((HERE / 'source-manifest.json').read_text()) +sha = lambda data: hashlib.sha256(data).hexdigest() +def git(*args): + return subprocess.check_output(['git', '-C', str(ROOT), *args], text=True).strip() +assert git('rev-parse', 'HEAD') == cfg['base_revision'] +assert git('ls-remote', 'origin', 'refs/heads/main').split()[0] == cfg['base_revision'] +assert set(git('diff', '--name-only', 'HEAD').splitlines()) <= {'.custodian-brief.md', 'WORK-RECORDS.md'} +tracked = git('ls-files').splitlines() +protected = {name: sha((ROOT / name).read_bytes()) for name in tracked + if (ROOT / name).is_file() and name not in {row['path'] for row in cfg['files']}} +index = git('ls-files', '--stage', '--', '.custodian-brief.md', 'WORK-RECORDS.md') +plan = 'workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md' +before = re.findall(r'```task\n.*?```', (ROOT / plan).read_text(), re.S) +assert len(before) == 9 +for row in cfg['files']: + current = ROOT / row['path'] + if row['before_sha256'] is None: + assert not current.exists(), row['path'] + else: + assert sha(current.read_bytes()) == row['before_sha256'], row['path'] + assert sha(Path(row['source']).read_bytes()) == row['sha256'], row['path'] +for row in cfg['files']: + target = ROOT / row['path'] + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(Path(row['source']).read_bytes()) +after = re.findall(r'```task\n.*?```', (ROOT / plan).read_text(), re.S) +assert before == after +assert all(sha((ROOT / name).read_bytes()) == value for name, value in protected.items()) +assert git('ls-files', '--stage', '--', '.custodian-brief.md', 'WORK-RECORDS.md') == index +receipt = dict(owner=cfg['owner'], base_revision='git:' + cfg['base_revision'], + files=cfg['files'], preserved_other_tracked_files=len(protected), + original_task_uuids_preserved=9, status_change=None, + native_actions=False, independent_owner_review=False, operational_admission=False) +(HERE / 'source-application.json').write_text(json.dumps(receipt, indent=2) + '\n') +publication = dict(owner=cfg['owner'], base_revision=cfg['base_revision'], + message='Verify Forgejo PostgreSQL operations and preserve tracing hook context', + files=[dict(path=row['path'], sha256=row['sha256']) for row in cfg['files']]) +(HERE / 'publication-inputs.json').write_text(json.dumps(publication, indent=2) + '\n') +print(json.dumps(dict(status='source_applied', files=len(cfg['files']), native_actions=False))) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.json new file mode 100644 index 0000000..a9e0eef --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.json @@ -0,0 +1,393 @@ +{ + "repo_slug": "railiance-apps", + "repo_path": "/home/worsch/railiance-apps", + "source_revision": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager warning: .repo-classification.yaml: category is project but slug is not prj-*; not every category:project is a prj- repo", + "file_path": ".repo-classification.yaml", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager missing: README.md: README.md is required for flavor project", + "file_path": "README.md", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['RAILIANCE-WP', 'RAPPS-WP']", + "file_path": "workplans/", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-1: task id 'RAILIANCE-WP-0004-I01' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-1", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-2: task id 'RAILIANCE-WP-0004-I02' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-2", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-3: task id 'RAILIANCE-WP-0004-I03' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-3", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-4: task id 'RAILIANCE-WP-0004-I04' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-4", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-5: task id 'RAILIANCE-WP-0004-I05' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-5", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-6: task id 'RAILIANCE-WP-0004-I06' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-6", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager contradictory: workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-7: task id 'RAILIANCE-WP-0004-I07' is not accepted by the canon kind registry", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-7", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I01' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I02' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I03' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I04' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I05' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I06' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-31", + "message": "[workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md] id 'RAILIANCE-WP-0004-I07' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-33", + "message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)", + "file_path": "WORK-RECORDS.md", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-39", + "message": "Wait task 'RAPPS-WP-0015-T04' in workplan 'RAPPS-WP-0015' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait", + "file_path": "workplans/RAPPS-WP-0015-coulomb-personal-content-release.md#RAPPS-WP-0015-T04", + "db_id": "", + "file_value": "wait", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0038' is not linked to State Hub", + "file_path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "db_id": "", + "file_value": "KEY-WP-0038", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-39", + "message": "Human wait task 'RAPPS-WP-0016-T04' in workplan 'RAPPS-WP-0016' has needs_human: true without decision_id \u2014 unrouted human gate", + "file_path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md#RAPPS-WP-0016-T04", + "db_id": "", + "file_value": "needs_human: true", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-22", + "message": "Task description drift 'RAPPS-WP-0016-T08': file content differs from DB (file wins)", + "file_path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md#RAPPS-WP-0016-T08", + "db_id": "4ab8a5e7-ab32-5ee2-aaf2-6da246d1b0c8", + "file_value": "Implement actual owner database primitives that record operation intent/outcome\nin the same durable database, compare co", + "db_value": "Implement actual owner database primitives that record operation intent/outcome\nin the same durable database, compare co", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RPF-WP-0058' is not linked to State Hub", + "file_path": "workplans/RAPPS-WP-0017-reuse-private-reader-release.md", + "db_id": "", + "file_value": "RPF-WP-0058", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'REUSE-WP-0025' is not linked to State Hub", + "file_path": "workplans/RAPPS-WP-0017-reuse-private-reader-release.md", + "db_id": "", + "file_value": "REUSE-WP-0025", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0006@retired-20260826' (id=1960b22b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "1960b22b-82ae-4b67-9fff-397bd38bdd65", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0005@retired-20260826' (id=685f1c18\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "685f1c18-33c0-400d-a2b1-e1dae0f27c3e", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0009@retired-20260826' (id=21c3fcdc\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "21c3fcdc-f014-4858-8028-0ac3038212b7", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0011@retired-20260826' (id=98cf42ae\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "98cf42ae-9b64-4736-97e1-bae325ded1f9", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0008@retired-20260826' (id=41b9deea\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "41b9deea-a935-4372-8916-b3981336f597", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0018@retired-20260826' (id=1b9af4e8\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "1b9af4e8-7579-5141-837b-c736b85204a0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0007@retired-20260826' (id=7da18dd8\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "7da18dd8-76b9-4a70-b9d7-de541afc65c0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0010@retired-20260826' (id=a4f301bb\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "a4f301bb-3f49-450a-8dbd-5cf6a16b8e0f", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread message(s) lack thread_id for supersession tracking: the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'RAIL-AP-WP-0001' uses prefix 'RAIL-AP-WP' but repo canonical prefix is 'RAPPS-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260605-RAIL-AP-WP-0001-gitea-container-registry.md", + "db_id": "", + "file_value": "RAIL-AP-WP-0001", + "db_value": "RAPPS-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'RAILIANCE-WP-0002' uses prefix 'RAILIANCE-WP' but repo canonical prefix is 'RAPPS-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-railiance-apps-WP-0002-vergabe-teilnahme-on-railiance01.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0002", + "db_value": "RAPPS-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'RAILIANCE-WP-0004' uses prefix 'RAILIANCE-WP' but repo canonical prefix is 'RAPPS-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0004", + "db_value": "RAPPS-WP", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 20/20", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-33 fixed: regenerated WORK-RECORDS.md", + "C-22 fixed: task 4ab8a5e7\u2026 description updated", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 29, + "info": 8 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.stderr.txt new file mode 100644 index 0000000..3345563 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/consistency.stderr.txt @@ -0,0 +1 @@ +WARN: git commit failed for writeback: Generated-only commits rejected: staged diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/projection.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/projection.json new file mode 100644 index 0000000..a1ac998 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/projection.json @@ -0,0 +1,172 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "railiance-apps", + "commit": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "requests": 2, + "elapsed_ms": 2055.9, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "railiance-apps", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [ + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I01", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-1" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I02", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-2" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I03", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-3" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I04", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-4" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I05", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-5" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I06", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-6" + }, + { + "kind": "task", + "id": "RAILIANCE-WP-0004-I07", + "source": "workplans/archived/260702-railiance-apps-WP-0004-app-deployment-improvements.md#task-block-7" + } + ], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "derived_commit": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "railiance-apps", + "commit": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "status": "applied", + "counts": { + "created": 0, + "updated": 20, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 0, + "external_edges": 5 + }, + "created": [], + "updated": [ + "RAIL-AP-WP-0001", + "RAILIANCE-WP-0002", + "RAILIANCE-WP-0004", + "RAPPS-WP-0001", + "RAPPS-WP-0002", + "RAPPS-WP-0003", + "RAPPS-WP-0004", + "RAPPS-WP-0005", + "RAPPS-WP-0006", + "RAPPS-WP-0007", + "RAPPS-WP-0008", + "RAPPS-WP-0009", + "RAPPS-WP-0010", + "RAPPS-WP-0011", + "RAPPS-WP-0012", + "RAPPS-WP-0013", + "RAPPS-WP-0014", + "RAPPS-WP-0015", + "RAPPS-WP-0016", + "RAPPS-WP-0017" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [], + "external_edges": [ + { + "workplan": "RAPPS-WP-0016", + "from": "RAPPS-WP-0016-T02", + "target": "RAPPS-WP-0016-T01", + "relationship_type": "starts_after", + "from_workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b", + "from_task_id": "13cd3aec-c87d-5d3f-a421-39d8ff131edd", + "to_workplan_id": null, + "to_task_id": "98004ed0-83e6-5f6e-9b18-15b8b2aec2e7" + }, + { + "workplan": "RAPPS-WP-0016", + "from": "RAPPS-WP-0016-T02", + "target": "KEY-WP-0038-T02", + "relationship_type": "starts_after", + "from_workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b", + "from_task_id": "13cd3aec-c87d-5d3f-a421-39d8ff131edd", + "to_workplan_id": null, + "to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" + }, + { + "workplan": "RAPPS-WP-0016", + "from": "RAPPS-WP-0016-T04", + "target": "RAPPS-WP-0016-T03", + "relationship_type": "starts_after", + "from_workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b", + "from_task_id": "32c54737-c40f-5fde-9b91-163f8dcb0068", + "to_workplan_id": null, + "to_task_id": "81f072aa-c316-511e-a2cd-21dd1b1d0a00" + }, + { + "workplan": "RAPPS-WP-0016", + "from": "RAPPS-WP-0016-T05", + "target": "RAPPS-WP-0016-T02", + "relationship_type": "starts_after", + "from_workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b", + "from_task_id": "728786c9-f1f9-5983-a6a4-dea21faf67f5", + "to_workplan_id": null, + "to_task_id": "13cd3aec-c87d-5d3f-a421-39d8ff131edd" + }, + { + "workplan": "RAPPS-WP-0016", + "from": "RAPPS-WP-0016-T03", + "target": "RAPPS-WP-0016-T02", + "relationship_type": "starts_after", + "from_workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b", + "from_task_id": "81f072aa-c316-511e-a2cd-21dd1b1d0a00", + "to_workplan_id": null, + "to_task_id": "13cd3aec-c87d-5d3f-a421-39d8ff131edd" + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/projection.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-commit.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-commit.json new file mode 100644 index 0000000..59c20bf --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-commit.json @@ -0,0 +1,25 @@ +{ + "exit_code": 0, + "commit": "308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "base": "3fb031a47ff5379358fd8cc230b40961b3b5d3a8", + "files": [ + { + "path": "build/forgejo/inputs.lock.json", + "sha256": "5a4d0e5126b6b9dd66fe2828ff78bfe6ac0cadc34b96ca739225d622b963b768" + }, + { + "path": "build/forgejo/forgejo-security-extension.patch", + "sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c" + }, + { + "path": "build/forgejo/README.md", + "sha256": "8e4edcfa386efe78e4df0b36f8463d229450cd12fc20b46efdba387f7f21bdfe" + }, + { + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "58d09d6af665bd7ad8981f87ccad5552d78f749432cfe21703d6c2a30f8bdbae" + } + ], + "stdout": "[main 308e83f] Verify Forgejo PostgreSQL operations and preserve tracing hook context\n 4 files changed, 521 insertions(+), 9 deletions(-)\n", + "stderr": "" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-inputs.json new file mode 100644 index 0000000..b19729b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/publication-inputs.json @@ -0,0 +1,23 @@ +{ + "owner": "railiance-apps", + "base_revision": "3fb031a47ff5379358fd8cc230b40961b3b5d3a8", + "message": "Verify Forgejo PostgreSQL operations and preserve tracing hook context", + "files": [ + { + "path": "build/forgejo/inputs.lock.json", + "sha256": "5a4d0e5126b6b9dd66fe2828ff78bfe6ac0cadc34b96ca739225d622b963b768" + }, + { + "path": "build/forgejo/forgejo-security-extension.patch", + "sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c" + }, + { + "path": "build/forgejo/README.md", + "sha256": "8e4edcfa386efe78e4df0b36f8463d229450cd12fc20b46efdba387f7f21bdfe" + }, + { + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "58d09d6af665bd7ad8981f87ccad5552d78f749432cfe21703d6c2a30f8bdbae" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-application.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-application.json new file mode 100644 index 0000000..2d9c187 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-application.json @@ -0,0 +1,36 @@ +{ + "owner": "railiance-apps", + "base_revision": "git:3fb031a47ff5379358fd8cc230b40961b3b5d3a8", + "files": [ + { + "path": "build/forgejo/inputs.lock.json", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/inputs.lock.json", + "before_sha256": "acf9859d1b1009d1afe0844f2068f9b95cfadb176591edce0ec42084604e4d65", + "sha256": "5a4d0e5126b6b9dd66fe2828ff78bfe6ac0cadc34b96ca739225d622b963b768" + }, + { + "path": "build/forgejo/forgejo-security-extension.patch", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/forgejo-security-extension.patch", + "before_sha256": "15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c" + }, + { + "path": "build/forgejo/README.md", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/README.md", + "before_sha256": "6250821cc41d10b92e6d91dde1f2b59b53a9d47d78ada3a597c9d039549c0da4", + "sha256": "8e4edcfa386efe78e4df0b36f8463d229450cd12fc20b46efdba387f7f21bdfe" + }, + { + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan.md", + "before_sha256": "95b48b308d5154f87355b2c230dc9990241aeeb37c2094e164952150ed2fd258", + "sha256": "58d09d6af665bd7ad8981f87ccad5552d78f749432cfe21703d6c2a30f8bdbae" + } + ], + "preserved_other_tracked_files": 224, + "original_task_uuids_preserved": 9, + "status_change": null, + "native_actions": false, + "independent_owner_review": false, + "operational_admission": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-manifest.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-manifest.json new file mode 100644 index 0000000..a4411cf --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/source-manifest.json @@ -0,0 +1,32 @@ +{ + "owner": "railiance-apps", + "base_revision": "3fb031a47ff5379358fd8cc230b40961b3b5d3a8", + "files": [ + { + "path": "build/forgejo/inputs.lock.json", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/inputs.lock.json", + "before_sha256": "acf9859d1b1009d1afe0844f2068f9b95cfadb176591edce0ec42084604e4d65", + "sha256": "5a4d0e5126b6b9dd66fe2828ff78bfe6ac0cadc34b96ca739225d622b963b768" + }, + { + "path": "build/forgejo/forgejo-security-extension.patch", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/forgejo-security-extension.patch", + "before_sha256": "15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c" + }, + { + "path": "build/forgejo/README.md", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/README.md", + "before_sha256": "6250821cc41d10b92e6d91dde1f2b59b53a9d47d78ada3a597c9d039549c0da4", + "sha256": "8e4edcfa386efe78e4df0b36f8463d229450cd12fc20b46efdba387f7f21bdfe" + }, + { + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan.md", + "before_sha256": "95b48b308d5154f87355b2c230dc9990241aeeb37c2094e164952150ed2fd258", + "sha256": "58d09d6af665bd7ad8981f87ccad5552d78f749432cfe21703d6c2a30f8bdbae" + } + ], + "native_actions": false, + "independent_owner_review": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan-preimage.md b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan-preimage.md new file mode 100644 index 0000000..e733dd0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan-preimage.md @@ -0,0 +1,283 @@ +--- +id: RAPPS-WP-0016 +type: workplan +title: "Forgejo sign-in through NetKingdom, before the forge closes" +domain: infotech +repo: railiance-apps +status: active +flavor: implementation +owner: claude-code +topic_slug: railiance +created: "2026-10-08" +updated: "2026-10-18" +depends_on: + - KEY-WP-0038 +related_repos: + - prj-forgejo-org-refactor + - key-cape + - railiance-platform +state_hub_workstream_id: "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b" +--- + +# RAPPS-WP-0016 - Forgejo sign-in through NetKingdom, before the forge closes + +## Why + +`forgejo.coulomb.social` is closing: private by default, no anonymous access +(prj-forgejo-org-refactor ORGREF-WP-0002, decision +`decisions/forge-visibility-policy.md`). The founder's condition is that sign-in +runs through NetKingdom first. railiance-apps owns the Forgejo Helm release +(`docs/forgejo-on-railiance01.md`: S5 Helm release, ingress, operator Makefile), +so the Forgejo side lands here. This plan is ORGREF-WP-0002-T05. The NetKingdom +side is key-cape `KEY-WP-0038`. + +## Boundaries + +- **Founder guardrail:** tighten gradually; never lock anyone out; never risk + assets; never reach a state that is hard to repair. Every step keeps the + current sign-in path working until the new one is proven. +- **No visibility change** in this plan. Repositories, packages and + `REQUIRE_SIGNIN_VIEW` are ORGREF-WP-0002-T08. +- **Configuration in git.** The authentication source is declared in + `helm/forgejo-values.yaml` (`gitea.oauth`), with its secret drawn from an + ExternalSecret over OpenBao. No click-ops, and no secret value in git. +- Open registration is already disabled (`service.DISABLE_REGISTRATION: true`). + This plan keeps it that way. + +## Tasks + +```task +id: RAPPS-WP-0016-T01 +status: done +priority: high +state_hub_task_id: "98004ed0-83e6-5f6e-9b18-15b8b2aec2e7" +``` + +### T01 — Make the local administrator a sealed break-glass + +Before anything about sign-in changes: confirm the local administrator account +works, keep its credential only in OpenBao, and document recovery (who, how, +where it is logged) in `docs/forgejo-on-railiance01.md`. Verify one break-glass +sign-in and record it without the credential. + +Acceptance: a recorded break-glass sign-in, with the recovery path written down. +This is the "never lock ourselves out" floor for every later step. + +Done 2026-10-17: founder sealed `forgejo_admin` into OpenBao +`platform/workloads/forgejo/break-glass-admin` (version 1) and verified a local +sign-in (HTTP 200, site admin): `docs/evidence/2026-10-17-forgejo-breakglass-signin.json`. +Recovery path: `docs/forgejo-on-railiance01.md` § Break-glass administrator. + +```task +id: RAPPS-WP-0016-T02 +status: wait +depends_on: [RAPPS-WP-0016-T01, KEY-WP-0038-T02] +blocking_reason: "Break-glass done (T01); needs the NetKingdom client registered with its secret in custody (KEY-WP-0038-T02)." +priority: high +state_hub_task_id: "13cd3aec-c87d-5d3f-a421-39d8ff131edd" +``` + +### T02 — Add the NetKingdom authentication source + +- an ExternalSecret delivering the `forgejo` client secret from OpenBao to a + Secret in the `forgejo` namespace. Custody path is + `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, read by ESO through + Kubernetes-auth role `external-secrets-keycape-forgejo-forgejo`, which carries + only policy `workload-kv-read-keycape-forgejo-forgejo` (net-kingdom 074b3a0; + replaces the earlier `platform/workloads/forgejo/netkingdom-oidc-client` proposal). The + SA/SecretStore/ExternalSecret shape follows the NK-WP-0048 + `deliver-consumer-secret` receipt contract + (`docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json`) +- `gitea.oauth`: name `netkingdom` (must match the redirect URI registered in + key-cape), provider `openidConnect`, auto-discovery from + `https://kc.coulomb.social/.well-known/openid-configuration`, scopes + `openid profile email groups`, group-claim name `groups` +- `oauth2_client` settings: no automatic account creation for unknown subjects; + account linking to existing local accounts by username after a successful + local sign-in (so the founder and agent accounts keep their history) +- Helm upgrade with the previous revision noted for rollback + +Local password sign-in stays enabled. + +Acceptance: the "Sign in with netkingdom" option appears. Local sign-in still +works. Rollback is one `helm rollback` to the noted revision. + +```task +id: RAPPS-WP-0016-T03 +status: wait +depends_on: [RAPPS-WP-0016-T02] +blocking_reason: "Linking and team mapping follow a working authentication source." +priority: high +state_hub_task_id: "81f072aa-c316-511e-a2cd-21dd1b1d0a00" +``` + +### T03 — Link accounts and map groups to teams + +- The founder links their account through NetKingdom (MFA), then signs in again + through NetKingdom only and sees the same repositories, organisations and + admin rights. +- Map NetKingdom groups (names agreed in KEY-WP-0038-T01) to organisation teams: + owners and readers at least. Create the read-only teams that + ORGREF-WP-0002-T06 consumers will use. +- Apply KEY-WP-0038-T03's decision on agents and services: NetKingdom sign-in, or + scoped tokens bound to service identities. + +Acceptance: founder and one agent work through NetKingdom; group-to-team mapping +verified by adding and removing one test membership; break-glass still works. + +```task +id: RAPPS-WP-0016-T04 +status: wait +depends_on: [RAPPS-WP-0016-T03] +needs_human: true +blocking_reason: "Restricting local password sign-in is a founder decision once NetKingdom sign-in is proven." +priority: medium +state_hub_task_id: "32c54737-c40f-5fde-9b91-163f8dcb0068" +``` + +### T04 — Decide on local password sign-in + +Once NetKingdom sign-in has been used for a while without problems, the founder +decides whether local password sign-in is turned off for everyone except +break-glass. This is not required to close the forge; it is the next tightening +step, taken separately. + +```task +id: RAPPS-WP-0016-T05 +status: wait +depends_on: [RAPPS-WP-0016-T02] +blocking_reason: "Waits for net-kingdom's answer to the Apps 1.3 needs-change record (the-custodian docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json: RAPPS-1.3-C1..C3) and the re-frozen 1.3 candidate." +priority: medium +state_hub_task_id: "728786c9-f1f9-5983-a6a4-dea21faf67f5" +``` + +### T05 — Apps owner seams for NK-WP-0048 consumer steps + +Implement the real Apps capabilities behind the `deliver-consumer-secret` and +`hand-over` adapters, as owner-run paths (option C): `owner_apply` / +`owner_remove` emitting `netkingdom.consumer-secret-delivery.v1` receipts from the +T02 objects; a consumer verifier that observes Forgejo sign-in and the T03 +positive/negative group probes; and a committed-evidence hand-over receipt store. +See the owner-review record for the contract. + +Acceptance: receipts produced from the live T02/T03 run validate against the +adapters' checks with no value-bearing keys. + +## Also in scope: stale node credential + +The k3s `registries.yaml` on railiance01 still holds a login for the frozen Gitea +host `92.205.130.254` (ORGREF-WP-0002-T02). Coordinate its removal with the node +owner when ORGREF-WP-0002-T06 adds the forge registry login, in the same change. + + +## T06 — Package the reviewed consumer source through the Apps build path + +```task +id: RAPPS-WP-0016-T06 +status: done +priority: high +state_hub_task_id: "08ab8921-7801-5309-b84b-bc2a3ceea1e4" +``` + +Completed 2026-10-18, source build tooling only. The existing Apps Makefile now +has explicit prepare, public-dependency acquisition, offline build, artifact +verification and local OCI export targets for the pinned official Forgejo11.0.3 +source plus reviewed multi-value claim patch. Source/toolchains/locks/compiler +inputs/base image are fixed. Third-party application source stays in a new /tmp +workspace; Apps holds the release input lock, patch and build recipe. + +The original build profile produced identical frontend assets, bindata sources +and complete static executable in two workspaces, and identical local OCI +archives in two no-cache exports. Independent review then found host process +argv visibility through /proc (APPSBUILD-R1-F01). The revised recipe adds a +private PID namespace; exact source and synthetic canary regression are separately +reviewed. The original full builds are historical r1-profile evidence; no full +r2 build is claimed. Original packet/review and failure history remain in +Custodian apps-forgejo-release-build-r1 and apps-release-independent-review-r1; +the correction is apps-forgejo-release-build-r2. This is coordinator source +integration after independent technical review, not formal owner assent or image +admission. Helm values, deployed image, local sign-in and visibility are unchanged. +NK-WP-0048-T39 remains progress for the full release acceptance scope. + +## T07 — Keep the client secret out of process arguments + +```task +id: RAPPS-WP-0016-T07 +status: progress +priority: high +state_hub_task_id: "786d6584-6db1-5a10-9324-d7b94db0df94" +``` + +Implement and independently review a real Forgejo add/update --secret-file path +and the actual chart helper/mount adaptation. Preserve the existing CLIENT_SECRET +key and custody route; use no agent-value argument or environment fallback. Test +bounded stable nofollow file reads, exact existing minted-secret encoding, +mutual exclusion/update preservation, fixed errors and actual Helm rendering. +Private mount identity/reader set and new package/type revisions need explicit +owner/runtime admission. The in-progress isolated candidate is Custodian +forgejo-private-input-r1; source completion is distinct from T02 native delivery. + +## T08 — Provide a transactional application-source inverse and durable recovery + +```task +id: RAPPS-WP-0016-T08 +status: progress +priority: high +state_hub_task_id: "4ab8a5e7-ab32-5ee2-aaf2-6da246d1b0c8" +``` + +Implement actual owner database primitives that record operation intent/outcome +in the same durable database, compare complete raw persisted before/post/current +source state privately, and serialize conflicting application writers at the DB +boundary. Never emit stored credential/config bytes or delete/detach linked users +or a preexisting/shared source. SQLite and PostgreSQL transaction semantics must +be tested against the real app packages; unsupported backends refuse. + +T05 and NK-WP-0048-T34 additionally require actual authenticated execution and +fresh inverse sessions, admitted evidence/recovery and exclusion of every release +writer, including Helm/runtime writers. The candidate DB primitive alone does +not establish those facilities. Helm rollback/omission does not remove persisted +OAuth sources: verify the exact app-state inverse before any image downgrade. +If links prevent removal, retain the extended image and reconcile. The earlier +T02 single-Helm-rollback statement is not sufficient recovery for this extension; +its historical text is retained and corrected here. + +## T09 — Enforce authenticated Access Engine decisions in the application path + +```task +id: RAPPS-WP-0016-T09 +status: progress +priority: high +state_hub_task_id: "d3804638-c73d-5b9f-a456-4262f62f9e6b" +``` + +Use the producer-owned decision format/canonical bytes and independently admitted +signer/policy/request pins. Forgejo must verify the caller's issuer/tenant and +assurance facts, obtain and authenticate the exact Access Engine resource/action +decision, and refuse missing, stale, wrong-pin, unavailable or malformed replies. +Keep the original three-group any-of access set as policy input; its local +compatibility gate is not a substitute PDP or an architecture exception. An +issuer allowed/denied claim cannot manufacture authorization. Source integration +needs real provider/callback/session wiring and meaningful producer-consumer +positive/adverse tests before re-freezing the case and package. Existing break- +glass/local account and linking behavior remain within the declared owner scope. + +2026-10-18 source continuation for T07/T08/T09: the digest-bound build +candidate now composes all 37 paths, including private secret-file input, actual +source-state transactions/private SQL hooks, the producer-owned Access Engine +Check client from ca0cb757 and guarded OIDC callback enforcement. The parser uses +the actual KeyCape typed assurance/authentication-time/provenance contract; its +actual issuer fixture/callback suite passes 71 test/subtest results, and selected +composed packages pass 93 with no skips. The previous incompatible-string-claim +rejection is retained. All patch postimages independently replay from the pinned +official archive. Complete release build/verification is tracked in Custodian +forgejo-keycape-claims-r1; earlier release outputs do not prove this revision. + +This is coordinator source integration of a tested build candidate; independent +owner review and formal operational admission remain open. T07's chart/private +mount admission, T08's fresh authenticated operation/inverse and release-wide +writer exclusion, T09's ongoing HTTP/API/Git/SSH paths, private evidence/retention, +PostgreSQL/native proof and actual issuer/factor/custody acceptance remain open. +No trust, deployed image, Helm values, source DB, user/link state or local sign-in +is changed by this build-source update. The preserved legacy patch is historical; +the input lock now explicitly selects forgejo-security-extension.patch. diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan.md b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan.md new file mode 100644 index 0000000..d53106e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/owner-integration/workplan.md @@ -0,0 +1,310 @@ +--- +id: RAPPS-WP-0016 +type: workplan +title: "Forgejo sign-in through NetKingdom, before the forge closes" +domain: infotech +repo: railiance-apps +status: active +flavor: implementation +owner: claude-code +topic_slug: railiance +created: "2026-10-08" +updated: "2026-10-18" +depends_on: + - KEY-WP-0038 +related_repos: + - prj-forgejo-org-refactor + - key-cape + - railiance-platform +state_hub_workstream_id: "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b" +--- + +# RAPPS-WP-0016 - Forgejo sign-in through NetKingdom, before the forge closes + +## Why + +`forgejo.coulomb.social` is closing: private by default, no anonymous access +(prj-forgejo-org-refactor ORGREF-WP-0002, decision +`decisions/forge-visibility-policy.md`). The founder's condition is that sign-in +runs through NetKingdom first. railiance-apps owns the Forgejo Helm release +(`docs/forgejo-on-railiance01.md`: S5 Helm release, ingress, operator Makefile), +so the Forgejo side lands here. This plan is ORGREF-WP-0002-T05. The NetKingdom +side is key-cape `KEY-WP-0038`. + +## Boundaries + +- **Founder guardrail:** tighten gradually; never lock anyone out; never risk + assets; never reach a state that is hard to repair. Every step keeps the + current sign-in path working until the new one is proven. +- **No visibility change** in this plan. Repositories, packages and + `REQUIRE_SIGNIN_VIEW` are ORGREF-WP-0002-T08. +- **Configuration in git.** The authentication source is declared in + `helm/forgejo-values.yaml` (`gitea.oauth`), with its secret drawn from an + ExternalSecret over OpenBao. No click-ops, and no secret value in git. +- Open registration is already disabled (`service.DISABLE_REGISTRATION: true`). + This plan keeps it that way. + +## Tasks + +```task +id: RAPPS-WP-0016-T01 +status: done +priority: high +state_hub_task_id: "98004ed0-83e6-5f6e-9b18-15b8b2aec2e7" +``` + +### T01 — Make the local administrator a sealed break-glass + +Before anything about sign-in changes: confirm the local administrator account +works, keep its credential only in OpenBao, and document recovery (who, how, +where it is logged) in `docs/forgejo-on-railiance01.md`. Verify one break-glass +sign-in and record it without the credential. + +Acceptance: a recorded break-glass sign-in, with the recovery path written down. +This is the "never lock ourselves out" floor for every later step. + +Done 2026-10-17: founder sealed `forgejo_admin` into OpenBao +`platform/workloads/forgejo/break-glass-admin` (version 1) and verified a local +sign-in (HTTP 200, site admin): `docs/evidence/2026-10-17-forgejo-breakglass-signin.json`. +Recovery path: `docs/forgejo-on-railiance01.md` § Break-glass administrator. + +```task +id: RAPPS-WP-0016-T02 +status: wait +depends_on: [RAPPS-WP-0016-T01, KEY-WP-0038-T02] +blocking_reason: "Break-glass done (T01); needs the NetKingdom client registered with its secret in custody (KEY-WP-0038-T02)." +priority: high +state_hub_task_id: "13cd3aec-c87d-5d3f-a421-39d8ff131edd" +``` + +### T02 — Add the NetKingdom authentication source + +- an ExternalSecret delivering the `forgejo` client secret from OpenBao to a + Secret in the `forgejo` namespace. Custody path is + `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, read by ESO through + Kubernetes-auth role `external-secrets-keycape-forgejo-forgejo`, which carries + only policy `workload-kv-read-keycape-forgejo-forgejo` (net-kingdom 074b3a0; + replaces the earlier `platform/workloads/forgejo/netkingdom-oidc-client` proposal). The + SA/SecretStore/ExternalSecret shape follows the NK-WP-0048 + `deliver-consumer-secret` receipt contract + (`docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json`) +- `gitea.oauth`: name `netkingdom` (must match the redirect URI registered in + key-cape), provider `openidConnect`, auto-discovery from + `https://kc.coulomb.social/.well-known/openid-configuration`, scopes + `openid profile email groups`, group-claim name `groups` +- `oauth2_client` settings: no automatic account creation for unknown subjects; + account linking to existing local accounts by username after a successful + local sign-in (so the founder and agent accounts keep their history) +- Helm upgrade with the previous revision noted for rollback + +Local password sign-in stays enabled. + +Acceptance: the "Sign in with netkingdom" option appears. Local sign-in still +works. Rollback is one `helm rollback` to the noted revision. + +```task +id: RAPPS-WP-0016-T03 +status: wait +depends_on: [RAPPS-WP-0016-T02] +blocking_reason: "Linking and team mapping follow a working authentication source." +priority: high +state_hub_task_id: "81f072aa-c316-511e-a2cd-21dd1b1d0a00" +``` + +### T03 — Link accounts and map groups to teams + +- The founder links their account through NetKingdom (MFA), then signs in again + through NetKingdom only and sees the same repositories, organisations and + admin rights. +- Map NetKingdom groups (names agreed in KEY-WP-0038-T01) to organisation teams: + owners and readers at least. Create the read-only teams that + ORGREF-WP-0002-T06 consumers will use. +- Apply KEY-WP-0038-T03's decision on agents and services: NetKingdom sign-in, or + scoped tokens bound to service identities. + +Acceptance: founder and one agent work through NetKingdom; group-to-team mapping +verified by adding and removing one test membership; break-glass still works. + +```task +id: RAPPS-WP-0016-T04 +status: wait +depends_on: [RAPPS-WP-0016-T03] +needs_human: true +blocking_reason: "Restricting local password sign-in is a founder decision once NetKingdom sign-in is proven." +priority: medium +state_hub_task_id: "32c54737-c40f-5fde-9b91-163f8dcb0068" +``` + +### T04 — Decide on local password sign-in + +Once NetKingdom sign-in has been used for a while without problems, the founder +decides whether local password sign-in is turned off for everyone except +break-glass. This is not required to close the forge; it is the next tightening +step, taken separately. + +```task +id: RAPPS-WP-0016-T05 +status: wait +depends_on: [RAPPS-WP-0016-T02] +blocking_reason: "Waits for net-kingdom's answer to the Apps 1.3 needs-change record (the-custodian docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json: RAPPS-1.3-C1..C3) and the re-frozen 1.3 candidate." +priority: medium +state_hub_task_id: "728786c9-f1f9-5983-a6a4-dea21faf67f5" +``` + +### T05 — Apps owner seams for NK-WP-0048 consumer steps + +Implement the real Apps capabilities behind the `deliver-consumer-secret` and +`hand-over` adapters, as owner-run paths (option C): `owner_apply` / +`owner_remove` emitting `netkingdom.consumer-secret-delivery.v1` receipts from the +T02 objects; a consumer verifier that observes Forgejo sign-in and the T03 +positive/negative group probes; and a committed-evidence hand-over receipt store. +See the owner-review record for the contract. + +Acceptance: receipts produced from the live T02/T03 run validate against the +adapters' checks with no value-bearing keys. + +## Also in scope: stale node credential + +The k3s `registries.yaml` on railiance01 still holds a login for the frozen Gitea +host `92.205.130.254` (ORGREF-WP-0002-T02). Coordinate its removal with the node +owner when ORGREF-WP-0002-T06 adds the forge registry login, in the same change. + + +## T06 — Package the reviewed consumer source through the Apps build path + +```task +id: RAPPS-WP-0016-T06 +status: done +priority: high +state_hub_task_id: "08ab8921-7801-5309-b84b-bc2a3ceea1e4" +``` + +Completed 2026-10-18, source build tooling only. The existing Apps Makefile now +has explicit prepare, public-dependency acquisition, offline build, artifact +verification and local OCI export targets for the pinned official Forgejo11.0.3 +source plus reviewed multi-value claim patch. Source/toolchains/locks/compiler +inputs/base image are fixed. Third-party application source stays in a new /tmp +workspace; Apps holds the release input lock, patch and build recipe. + +The original build profile produced identical frontend assets, bindata sources +and complete static executable in two workspaces, and identical local OCI +archives in two no-cache exports. Independent review then found host process +argv visibility through /proc (APPSBUILD-R1-F01). The revised recipe adds a +private PID namespace; exact source and synthetic canary regression are separately +reviewed. The original full builds are historical r1-profile evidence; no full +r2 build is claimed. Original packet/review and failure history remain in +Custodian apps-forgejo-release-build-r1 and apps-release-independent-review-r1; +the correction is apps-forgejo-release-build-r2. This is coordinator source +integration after independent technical review, not formal owner assent or image +admission. Helm values, deployed image, local sign-in and visibility are unchanged. +NK-WP-0048-T39 remains progress for the full release acceptance scope. + +## T07 — Keep the client secret out of process arguments + +```task +id: RAPPS-WP-0016-T07 +status: progress +priority: high +state_hub_task_id: "786d6584-6db1-5a10-9324-d7b94db0df94" +``` + +Implement and independently review a real Forgejo add/update --secret-file path +and the actual chart helper/mount adaptation. Preserve the existing CLIENT_SECRET +key and custody route; use no agent-value argument or environment fallback. Test +bounded stable nofollow file reads, exact existing minted-secret encoding, +mutual exclusion/update preservation, fixed errors and actual Helm rendering. +Private mount identity/reader set and new package/type revisions need explicit +owner/runtime admission. The in-progress isolated candidate is Custodian +forgejo-private-input-r1; source completion is distinct from T02 native delivery. + +## T08 — Provide a transactional application-source inverse and durable recovery + +```task +id: RAPPS-WP-0016-T08 +status: progress +priority: high +state_hub_task_id: "4ab8a5e7-ab32-5ee2-aaf2-6da246d1b0c8" +``` + +Implement actual owner database primitives that record operation intent/outcome +in the same durable database, compare complete raw persisted before/post/current +source state privately, and serialize conflicting application writers at the DB +boundary. Never emit stored credential/config bytes or delete/detach linked users +or a preexisting/shared source. SQLite and PostgreSQL transaction semantics must +be tested against the real app packages; unsupported backends refuse. + +T05 and NK-WP-0048-T34 additionally require actual authenticated execution and +fresh inverse sessions, admitted evidence/recovery and exclusion of every release +writer, including Helm/runtime writers. The candidate DB primitive alone does +not establish those facilities. Helm rollback/omission does not remove persisted +OAuth sources: verify the exact app-state inverse before any image downgrade. +If links prevent removal, retain the extended image and reconcile. The earlier +T02 single-Helm-rollback statement is not sufficient recovery for this extension; +its historical text is retained and corrected here. + +2026-10-18 PostgreSQL/source-context continuation for T08: actual Forgejo model +schemas, OAuth configuration types and production DB initialization run on a +disposable PostgreSQL 16.14 instance. All 37 test/subtest results pass across +default, explicit-public and named schemas. Separate client processes prove +rollback after process death immediately following INSERT, committed apply +survival through a real server restart, and subsequent reconciliation/inverse. +Exact raw restoration, private drift, linked-user/external-link refusal, trigger +rollback, table-writer exclusion and concurrent preparation are covered. +Controlled copies with the table lock or private-query context removed fail the +corresponding tests in all three schema profiles. All owned fixture containers +and volumes were removed; no deployed database or real credential was used. + +The diagnostics check exposed a real tracing-hook composition panic. The new +40-path build candidate publishes the task context into XORM's shared query +context so subsequent hooks preserve it. Actual private/ordinary SQLite traces +and PostgreSQL driver-error hooks cover the correction. Six focused SQLite DB +checks and all 94 selected composed package results pass without skips. The +previous panic, fixture/compilation failures and refinement history remain in +Custodian forgejo-postgres-source-r1. Complete release evidence uses the new +feb10793 patch; the prior frozen artifact is historical. + +This proves bounded source/database mechanics. T08 remains progress for current +authenticated initiating/inverse sessions, release-wide writer exclusion, private +evidence/custody/retention and native deployed-database acceptance. Independent +owner review and exact package/type/runtime admission remain open. T07/T09 and +all original task UUIDs and statuses are preserved. + +## T09 — Enforce authenticated Access Engine decisions in the application path + +```task +id: RAPPS-WP-0016-T09 +status: progress +priority: high +state_hub_task_id: "d3804638-c73d-5b9f-a456-4262f62f9e6b" +``` + +Use the producer-owned decision format/canonical bytes and independently admitted +signer/policy/request pins. Forgejo must verify the caller's issuer/tenant and +assurance facts, obtain and authenticate the exact Access Engine resource/action +decision, and refuse missing, stale, wrong-pin, unavailable or malformed replies. +Keep the original three-group any-of access set as policy input; its local +compatibility gate is not a substitute PDP or an architecture exception. An +issuer allowed/denied claim cannot manufacture authorization. Source integration +needs real provider/callback/session wiring and meaningful producer-consumer +positive/adverse tests before re-freezing the case and package. Existing break- +glass/local account and linking behavior remain within the declared owner scope. + +2026-10-18 source continuation for T07/T08/T09: the digest-bound build +candidate now composes all 37 paths, including private secret-file input, actual +source-state transactions/private SQL hooks, the producer-owned Access Engine +Check client from ca0cb757 and guarded OIDC callback enforcement. The parser uses +the actual KeyCape typed assurance/authentication-time/provenance contract; its +actual issuer fixture/callback suite passes 71 test/subtest results, and selected +composed packages pass 93 with no skips. The previous incompatible-string-claim +rejection is retained. All patch postimages independently replay from the pinned +official archive. Complete release build/verification is tracked in Custodian +forgejo-keycape-claims-r1; earlier release outputs do not prove this revision. + +This is coordinator source integration of a tested build candidate; independent +owner review and formal operational admission remain open. T07's chart/private +mount admission, T08's fresh authenticated operation/inverse and release-wide +writer exclusion, T09's ongoing HTTP/API/Git/SSH paths, private evidence/retention, +PostgreSQL/native proof and actual issuer/factor/custody acceptance remain open. +No trust, deployed image, Helm values, source DB, user/link state or local sign-in +is changed by this build-source update. The preserved legacy patch is historical; +the input lock now explicitly selects forgejo-security-extension.patch. diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt1.json new file mode 100644 index 0000000..cfe55f0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt1.json @@ -0,0 +1,163 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 13.81, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:852bf5cd2d2a8504d3b82723e09f296436e546fefcf6e5c2280e76f2ef1a7f17", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:a58899fab4b0355a6d9436ab726b1aeada01a2987856b3490a15cf3d33984a88" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt1.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt2.json new file mode 100644 index 0000000..24866a7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt2.json @@ -0,0 +1,163 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 5.88, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt2/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt2", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt2", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:0475dfa7d5e31733321134345ff90a3cd9fd8049db9c2b7d54ef1a71c0d70afd", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:9eadebf02dabd91ed50ddd590b62bda1ee4a89cd824069495603ad62b7d144ec" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt2.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt2.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt3.json new file mode 100644 index 0000000..fe64b95 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt3.json @@ -0,0 +1,163 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 6.08, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt3/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt3", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt3", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:b1da12376c4ee8f30e5b8302236fc77b30dd0b39b6c1c570134508bce15f55d8" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt3.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt3.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt4.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt4.json new file mode 100644 index 0000000..e5c9f97 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt4.json @@ -0,0 +1,197 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 12.52, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt4.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt4.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt5.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt5.json new file mode 100644 index 0000000..7dcd566 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt5.json @@ -0,0 +1,197 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 6.03, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/db", + "/work/source/models/db", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt5.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt5.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt6.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt6.json new file mode 100644 index 0000000..70599ba --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt6.json @@ -0,0 +1,200 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 15.24, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock/db", + "/work/source/models/db", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-control-lock", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:137bcc05baf724abfcb9c48a2ef45d5064a6a163c4ba98269364eafb9a1096cd", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "omit-postgres-lock", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:137bcc05baf724abfcb9c48a2ef45d5064a6a163c4ba98269364eafb9a1096cd", + "production_source_changed": false + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:82043a82a461a942ff747c59238a3113b5d06de06a2d927c7946df3b22d89498" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt6.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt6.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt7.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt7.json new file mode 100644 index 0000000..0c34dad --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt7.json @@ -0,0 +1,200 @@ +{ + "action": "build", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 7.8, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private/db", + "/work/source/models/db", + "--", + "go", + "test", + "-c", + "-p", + "4", + "-tags", + "sqlite sqlite_unlock_notify source_operation_postgres", + "-o", + "/pg-work/models-auth.test", + "./models/auth" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-control-private", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:0b9e248b685a950cfbf31f76aafd54e1b99272402a2f05bc6d8b742bc2657d82", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "omit-private-context", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:0b9e248b685a950cfbf31f76aafd54e1b99272402a2f05bc6d8b742bc2657d82", + "production_source_changed": false + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:e5acaf24f87a94f58b1cd9000f04a08186b35f29e6e848835ecd3c7105a9de72" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt7.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-build-attempt7.log new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.json new file mode 100644 index 0000000..951a4d1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.json @@ -0,0 +1,201 @@ +{ + "action": "combined-regressions", + "returncode": 1, + "expected_returncode": 0, + "outcome_matches": false, + "seconds": 113.49, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/db", + "/work/source/models/db", + "--", + "go", + "test", + "-p", + "1", + "-json", + "-tags", + "sqlite sqlite_unlock_notify", + "./services/auth/source/oauth2", + "./cmd", + "./models/auth", + "./models/db", + "-run", + "TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQL|TestTracingContext", + "-count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.log new file mode 100644 index 0000000..49b7ae0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt1.log @@ -0,0 +1,437 @@ +{"Time":"2026-10-18T01:08:37.942357346Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"} +{"Time":"2026-10-18T01:08:38.02991623Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"} +{"Time":"2026-10-18T01:08:38.029972101Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"} +{"Time":"2026-10-18T01:08:38.030052643Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"} +{"Time":"2026-10-18T01:08:38.030060128Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"} +{"Time":"2026-10-18T01:08:38.032547509Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.032605661Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0} +{"Time":"2026-10-18T01:08:38.032622902Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"} +{"Time":"2026-10-18T01:08:38.032633263Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"} +{"Time":"2026-10-18T01:08:38.03287782Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.032903393Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0} +{"Time":"2026-10-18T01:08:38.032916089Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"} +{"Time":"2026-10-18T01:08:38.03292388Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"} +{"Time":"2026-10-18T01:08:38.033385888Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.033415275Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0} +{"Time":"2026-10-18T01:08:38.033425248Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"} +{"Time":"2026-10-18T01:08:38.033432543Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"} +{"Time":"2026-10-18T01:08:38.033828689Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.033863369Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0} +{"Time":"2026-10-18T01:08:38.033873279Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"} +{"Time":"2026-10-18T01:08:38.033903649Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"} +{"Time":"2026-10-18T01:08:38.034191579Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.0342135Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0} +{"Time":"2026-10-18T01:08:38.034221155Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"} +{"Time":"2026-10-18T01:08:38.034226872Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"} +{"Time":"2026-10-18T01:08:38.034536047Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.034556804Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0} +{"Time":"2026-10-18T01:08:38.034563928Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"} +{"Time":"2026-10-18T01:08:38.034569356Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"} +{"Time":"2026-10-18T01:08:38.034897584Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.034920632Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0} +{"Time":"2026-10-18T01:08:38.03492827Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"} +{"Time":"2026-10-18T01:08:38.034933825Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"} +{"Time":"2026-10-18T01:08:38.035216317Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.035238716Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0} +{"Time":"2026-10-18T01:08:38.035247949Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"} +{"Time":"2026-10-18T01:08:38.035253477Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"} +{"Time":"2026-10-18T01:08:38.035604437Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.035655493Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0} +{"Time":"2026-10-18T01:08:38.035668388Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"} +{"Time":"2026-10-18T01:08:38.035674916Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"} +{"Time":"2026-10-18T01:08:38.035949068Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.035980105Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0} +{"Time":"2026-10-18T01:08:38.036118619Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"} +{"Time":"2026-10-18T01:08:38.036129954Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"} +{"Time":"2026-10-18T01:08:38.036407766Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.036427532Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0} +{"Time":"2026-10-18T01:08:38.036434971Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"} +{"Time":"2026-10-18T01:08:38.036444196Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"} +{"Time":"2026-10-18T01:08:38.036752288Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.036769718Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0} +{"Time":"2026-10-18T01:08:38.036777076Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"} +{"Time":"2026-10-18T01:08:38.036782613Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"} +{"Time":"2026-10-18T01:08:38.037139757Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.037172607Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0} +{"Time":"2026-10-18T01:08:38.037185204Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"} +{"Time":"2026-10-18T01:08:38.037191066Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"} +{"Time":"2026-10-18T01:08:38.037586335Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.037615515Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0} +{"Time":"2026-10-18T01:08:38.038228381Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.038255127Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.038263323Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"} +{"Time":"2026-10-18T01:08:38.038267769Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"} +{"Time":"2026-10-18T01:08:38.038744348Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.038763465Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0} +{"Time":"2026-10-18T01:08:38.038769597Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"} +{"Time":"2026-10-18T01:08:38.038777018Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"} +{"Time":"2026-10-18T01:08:38.039129691Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.039146022Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0} +{"Time":"2026-10-18T01:08:38.039151396Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"} +{"Time":"2026-10-18T01:08:38.039155535Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"} +{"Time":"2026-10-18T01:08:38.039162406Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"} +{"Time":"2026-10-18T01:08:38.039166383Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"} +{"Time":"2026-10-18T01:08:38.039589896Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.039619788Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0} +{"Time":"2026-10-18T01:08:38.039626226Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"} +{"Time":"2026-10-18T01:08:38.03964601Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"} +{"Time":"2026-10-18T01:08:38.040082661Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.040112806Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0} +{"Time":"2026-10-18T01:08:38.040119533Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"} +{"Time":"2026-10-18T01:08:38.040124727Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"} +{"Time":"2026-10-18T01:08:38.040412575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.040441863Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0} +{"Time":"2026-10-18T01:08:38.040450682Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"} +{"Time":"2026-10-18T01:08:38.040455552Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"} +{"Time":"2026-10-18T01:08:38.040935613Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.040977543Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0} +{"Time":"2026-10-18T01:08:38.040986488Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.040993333Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0} +{"Time":"2026-10-18T01:08:38.040998977Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"} +{"Time":"2026-10-18T01:08:38.041003793Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"} +{"Time":"2026-10-18T01:08:38.041969774Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.042022831Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0} +{"Time":"2026-10-18T01:08:38.042034039Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"} +{"Time":"2026-10-18T01:08:38.042063941Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"} +{"Time":"2026-10-18T01:08:38.042081281Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"} +{"Time":"2026-10-18T01:08:38.042087818Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"} +{"Time":"2026-10-18T01:08:38.060386001Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.02s)\n"} +{"Time":"2026-10-18T01:08:38.060433171Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.02} +{"Time":"2026-10-18T01:08:38.06044024Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"} +{"Time":"2026-10-18T01:08:38.060443811Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"} +{"Time":"2026-10-18T01:08:38.067923765Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.067970564Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.068057013Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"} +{"Time":"2026-10-18T01:08:38.068067193Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"} +{"Time":"2026-10-18T01:08:38.076490055Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.076544438Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.076556359Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (0.03s)\n"} +{"Time":"2026-10-18T01:08:38.076562022Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":0.03} +{"Time":"2026-10-18T01:08:38.076567946Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"} +{"Time":"2026-10-18T01:08:38.076572797Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"} +{"Time":"2026-10-18T01:08:38.07659654Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"} +{"Time":"2026-10-18T01:08:38.076602807Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"} +{"Time":"2026-10-18T01:08:38.083463046Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.083516031Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.083524084Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"} +{"Time":"2026-10-18T01:08:38.083528178Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"} +{"Time":"2026-10-18T01:08:38.090174922Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.090227925Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.090242686Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"} +{"Time":"2026-10-18T01:08:38.090247312Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"} +{"Time":"2026-10-18T01:08:38.097754175Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.097804672Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.097812778Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"} +{"Time":"2026-10-18T01:08:38.097817638Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"} +{"Time":"2026-10-18T01:08:38.105249939Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.105298993Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.105308101Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"} +{"Time":"2026-10-18T01:08:38.105313502Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"} +{"Time":"2026-10-18T01:08:38.112656204Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.112702697Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.112711795Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"} +{"Time":"2026-10-18T01:08:38.112717593Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"} +{"Time":"2026-10-18T01:08:38.12088967Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.120933746Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.120942799Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (0.04s)\n"} +{"Time":"2026-10-18T01:08:38.120948552Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":0.04} +{"Time":"2026-10-18T01:08:38.120956181Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"} +{"Time":"2026-10-18T01:08:38.120961077Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"} +{"Time":"2026-10-18T01:08:38.120965703Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"} +{"Time":"2026-10-18T01:08:38.120969599Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"} +{"Time":"2026-10-18T01:08:38.123261541Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.123328647Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0} +{"Time":"2026-10-18T01:08:38.123346096Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"} +{"Time":"2026-10-18T01:08:38.123354527Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"} +{"Time":"2026-10-18T01:08:38.125041159Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.12509896Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0} +{"Time":"2026-10-18T01:08:38.12513016Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"} +{"Time":"2026-10-18T01:08:38.125138708Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"} +{"Time":"2026-10-18T01:08:38.127340985Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.127380273Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0} +{"Time":"2026-10-18T01:08:38.12738893Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"} +{"Time":"2026-10-18T01:08:38.127394475Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"} +{"Time":"2026-10-18T01:08:38.129019429Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.129064686Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0} +{"Time":"2026-10-18T01:08:38.129071666Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"} +{"Time":"2026-10-18T01:08:38.129076183Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"} +{"Time":"2026-10-18T01:08:38.130278038Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.130306551Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0} +{"Time":"2026-10-18T01:08:38.130313539Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"} +{"Time":"2026-10-18T01:08:38.130317985Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"} +{"Time":"2026-10-18T01:08:38.13139377Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.131428315Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0} +{"Time":"2026-10-18T01:08:38.131436575Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"} +{"Time":"2026-10-18T01:08:38.131453157Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"} +{"Time":"2026-10-18T01:08:38.13267415Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.132719588Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0} +{"Time":"2026-10-18T01:08:38.132727072Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"} +{"Time":"2026-10-18T01:08:38.132731752Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"} +{"Time":"2026-10-18T01:08:38.13405843Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.134105735Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0} +{"Time":"2026-10-18T01:08:38.134123129Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"} +{"Time":"2026-10-18T01:08:38.134127827Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"} +{"Time":"2026-10-18T01:08:38.135942521Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.136077104Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0} +{"Time":"2026-10-18T01:08:38.136086707Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"} +{"Time":"2026-10-18T01:08:38.136122623Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"} +{"Time":"2026-10-18T01:08:38.13805575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.13809997Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0} +{"Time":"2026-10-18T01:08:38.138116887Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (0.02s)\n"} +{"Time":"2026-10-18T01:08:38.138122198Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":0.02} +{"Time":"2026-10-18T01:08:38.138143443Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"} +{"Time":"2026-10-18T01:08:38.138150224Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"} +{"Time":"2026-10-18T01:08:38.138630933Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"} +{"Time":"2026-10-18T01:08:38.138647218Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"} +{"Time":"2026-10-18T01:08:38.139089192Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.139124846Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0} +{"Time":"2026-10-18T01:08:38.139141573Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"} +{"Time":"2026-10-18T01:08:38.139150483Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"} +{"Time":"2026-10-18T01:08:38.139639224Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.139675113Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0} +{"Time":"2026-10-18T01:08:38.139685771Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"} +{"Time":"2026-10-18T01:08:38.139691226Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"} +{"Time":"2026-10-18T01:08:38.140187391Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.140250999Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0} +{"Time":"2026-10-18T01:08:38.140263966Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"} +{"Time":"2026-10-18T01:08:38.140269475Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"} +{"Time":"2026-10-18T01:08:38.140753837Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.14078501Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0} +{"Time":"2026-10-18T01:08:38.140794694Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"} +{"Time":"2026-10-18T01:08:38.140818545Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"} +{"Time":"2026-10-18T01:08:38.141227324Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.141247351Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0} +{"Time":"2026-10-18T01:08:38.141256494Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"} +{"Time":"2026-10-18T01:08:38.14126121Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"} +{"Time":"2026-10-18T01:08:38.14175302Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.141777358Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0} +{"Time":"2026-10-18T01:08:38.141786988Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"} +{"Time":"2026-10-18T01:08:38.141792065Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"} +{"Time":"2026-10-18T01:08:38.142257129Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.142284181Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0} +{"Time":"2026-10-18T01:08:38.142293893Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"} +{"Time":"2026-10-18T01:08:38.142301422Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"} +{"Time":"2026-10-18T01:08:38.142974782Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.14301747Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0} +{"Time":"2026-10-18T01:08:38.143027957Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"} +{"Time":"2026-10-18T01:08:38.143032096Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"} +{"Time":"2026-10-18T01:08:38.143567621Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.143613519Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0} +{"Time":"2026-10-18T01:08:38.143665495Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"} +{"Time":"2026-10-18T01:08:38.143682104Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"} +{"Time":"2026-10-18T01:08:38.144339272Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.14438893Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0} +{"Time":"2026-10-18T01:08:38.144404693Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"} +{"Time":"2026-10-18T01:08:38.144409968Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"} +{"Time":"2026-10-18T01:08:38.145033278Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.145075524Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0} +{"Time":"2026-10-18T01:08:38.145091052Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"} +{"Time":"2026-10-18T01:08:38.14510061Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"} +{"Time":"2026-10-18T01:08:38.145490465Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.145520294Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0} +{"Time":"2026-10-18T01:08:38.145527742Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"} +{"Time":"2026-10-18T01:08:38.14553813Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"} +{"Time":"2026-10-18T01:08:38.145833517Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.145853364Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0} +{"Time":"2026-10-18T01:08:38.145865032Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"} +{"Time":"2026-10-18T01:08:38.145877476Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"} +{"Time":"2026-10-18T01:08:38.14606701Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.14610426Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146110707Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"} +{"Time":"2026-10-18T01:08:38.146115171Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"} +{"Time":"2026-10-18T01:08:38.146244631Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.146272882Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146282342Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"} +{"Time":"2026-10-18T01:08:38.14628703Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"} +{"Time":"2026-10-18T01:08:38.146428322Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.146451262Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146459134Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"} +{"Time":"2026-10-18T01:08:38.146463435Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"} +{"Time":"2026-10-18T01:08:38.146472804Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.146517061Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146537863Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"} +{"Time":"2026-10-18T01:08:38.146548747Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01} +{"Time":"2026-10-18T01:08:38.146631282Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"} +{"Time":"2026-10-18T01:08:38.146644321Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"} +{"Time":"2026-10-18T01:08:38.146744863Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.146764431Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146803809Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"} +{"Time":"2026-10-18T01:08:38.146816208Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"} +{"Time":"2026-10-18T01:08:38.146828444Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.146835748Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0} +{"Time":"2026-10-18T01:08:38.146854883Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"} +{"Time":"2026-10-18T01:08:38.146861393Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"} +{"Time":"2026-10-18T01:08:38.151405928Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.15147482Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0} +{"Time":"2026-10-18T01:08:38.151489392Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"} +{"Time":"2026-10-18T01:08:38.151495731Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"} +{"Time":"2026-10-18T01:08:38.151511701Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.151535578Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0} +{"Time":"2026-10-18T01:08:38.151548581Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"} +{"Time":"2026-10-18T01:08:38.151556183Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"} +{"Time":"2026-10-18T01:08:38.151579925Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"} +{"Time":"2026-10-18T01:08:38.151600908Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0} +{"Time":"2026-10-18T01:08:38.151642234Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"} +{"Time":"2026-10-18T01:08:38.161114359Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t0.218s\n"} +{"Time":"2026-10-18T01:08:38.161169977Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":0.219} +{"Time":"2026-10-18T01:09:36.44937704Z","Action":"start","Package":"forgejo.org/cmd"} +{"Time":"2026-10-18T01:09:36.72251156Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.722695863Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/attachments\n"} +{"Time":"2026-10-18T01:09:36.722765837Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.72278141Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/avatars\n"} +{"Time":"2026-10-18T01:09:36.722791194Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.72279884Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/repo-avatars\n"} +{"Time":"2026-10-18T01:09:36.722806451Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.722813638Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/repo-archive\n"} +{"Time":"2026-10-18T01:09:36.72282096Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.722827876Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/packages\n"} +{"Time":"2026-10-18T01:09:36.722846578Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.722854179Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1279375576/actions_log\n"} +{"Time":"2026-10-18T01:09:36.722861168Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:09:36.722867985Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:09:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2012268276/b1034/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:09:36.933855782Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata"} +{"Time":"2026-10-18T01:09:36.93390342Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"=== RUN TestPrivateSecretMetadata\n"} +{"Time":"2026-10-18T01:09:36.933918659Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"--- PASS: TestPrivateSecretMetadata (0.00s)\n"} +{"Time":"2026-10-18T01:09:36.933933474Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Elapsed":0} +{"Time":"2026-10-18T01:09:36.934066785Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement"} +{"Time":"2026-10-18T01:09:36.934079148Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"=== RUN TestPrivateSecretOpenedFileReplacement\n"} +{"Time":"2026-10-18T01:09:36.93684219Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":" admin_auth_oauth_secret_linux_test.go:74: rename /run/writable-input/race-replacement /run/writable-input/race: no such file or directory\n"} +{"Time":"2026-10-18T01:09:36.936927006Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"--- FAIL: TestPrivateSecretOpenedFileReplacement (0.00s)\n"} +{"Time":"2026-10-18T01:09:36.936948233Z","Action":"fail","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Elapsed":0} +{"Time":"2026-10-18T01:09:36.937533425Z","Action":"output","Package":"forgejo.org/cmd","Output":"FAIL\n"} +{"Time":"2026-10-18T01:09:36.978993641Z","Action":"output","Package":"forgejo.org/cmd","Output":"FAIL\tforgejo.org/cmd\t0.529s\n"} +{"Time":"2026-10-18T01:09:36.979049521Z","Action":"fail","Package":"forgejo.org/cmd","Elapsed":0.53} +{"Time":"2026-10-18T01:09:59.109713139Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:09:59.339505364Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339577069Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/attachments\n"} +{"Time":"2026-10-18T01:09:59.339586744Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339596014Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/avatars\n"} +{"Time":"2026-10-18T01:09:59.339604157Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339612092Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/repo-avatars\n"} +{"Time":"2026-10-18T01:09:59.339619838Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339627674Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/repo-archive\n"} +{"Time":"2026-10-18T01:09:59.339635888Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339643724Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/packages\n"} +{"Time":"2026-10-18T01:09:59.339651398Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339659099Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1964458793/actions_log\n"} +{"Time":"2026-10-18T01:09:59.33970093Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:09:59.339723843Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:09:59 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2012268276/b1314/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:09:59.492752156Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion"} +{"Time":"2026-10-18T01:09:59.492810335Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"=== RUN TestSourceOperationDatabaseWriterExclusion\n"} +{"Time":"2026-10-18T01:09:59.594543385Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"--- PASS: TestSourceOperationDatabaseWriterExclusion (0.10s)\n"} +{"Time":"2026-10-18T01:09:59.594628435Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Elapsed":0.1} +{"Time":"2026-10-18T01:09:59.594650473Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging"} +{"Time":"2026-10-18T01:09:59.594656226Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"=== RUN TestSourceOperationRefusesActualXORMArgumentLogging\n"} +{"Time":"2026-10-18T01:09:59.594911618Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 01:09:59.594763 [SQL] BEGIN TRANSACTION [] - 150.209µs\n"} +{"Time":"2026-10-18T01:09:59.594942088Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 01:09:59.594857 [SQL] ROLLBACK [] - 25.772µs\n"} +{"Time":"2026-10-18T01:09:59.594963531Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"--- PASS: TestSourceOperationRefusesActualXORMArgumentLogging (0.00s)\n"} +{"Time":"2026-10-18T01:09:59.59497822Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Elapsed":0} +{"Time":"2026-10-18T01:09:59.597123297Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery"} +{"Time":"2026-10-18T01:09:59.597176156Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"=== RUN TestSourceOperationProcessRecovery\n"} +{"Time":"2026-10-18T01:10:02.249743726Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"--- PASS: TestSourceOperationProcessRecovery (2.65s)\n"} +{"Time":"2026-10-18T01:10:02.249844818Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Elapsed":2.65} +{"Time":"2026-10-18T01:10:02.249877542Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse"} +{"Time":"2026-10-18T01:10:02.249895423Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"=== RUN TestSourceOperationCreateAndInverse\n"} +{"Time":"2026-10-18T01:10:02.279279803Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"--- PASS: TestSourceOperationCreateAndInverse (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.279363177Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.27938067Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting"} +{"Time":"2026-10-18T01:10:02.279388479Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"=== RUN TestSourceOperationRestoresExactRawPreexisting\n"} +{"Time":"2026-10-18T01:10:02.301909786Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"--- PASS: TestSourceOperationRestoresExactRawPreexisting (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.30197086Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.301988894Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting"} +{"Time":"2026-10-18T01:10:02.301997127Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"=== RUN TestSourceOperationUnchangedNeverDeletesPreexisting\n"} +{"Time":"2026-10-18T01:10:02.333928333Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"--- PASS: TestSourceOperationUnchangedNeverDeletesPreexisting (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.333974736Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.333988235Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses"} +{"Time":"2026-10-18T01:10:02.33399461Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"=== RUN TestSourceOperationPrivateDriftRefuses\n"} +{"Time":"2026-10-18T01:10:02.334242557Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply"} +{"Time":"2026-10-18T01:10:02.334265416Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-apply\n"} +{"Time":"2026-10-18T01:10:02.359245801Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-apply (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.35930967Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.359333971Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse"} +{"Time":"2026-10-18T01:10:02.359353403Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-inverse\n"} +{"Time":"2026-10-18T01:10:02.387167083Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-inverse (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.387212133Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.387223144Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses (0.05s)\n"} +{"Time":"2026-10-18T01:10:02.387230493Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Elapsed":0.05} +{"Time":"2026-10-18T01:10:02.387241241Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses"} +{"Time":"2026-10-18T01:10:02.387247427Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"=== RUN TestSourceOperationStockCRUDDriftRefuses\n"} +{"Time":"2026-10-18T01:10:02.411254298Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"--- PASS: TestSourceOperationStockCRUDDriftRefuses (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.411303532Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.411332261Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion"} +{"Time":"2026-10-18T01:10:02.411338745Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion\n"} +{"Time":"2026-10-18T01:10:02.411353551Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user"} +{"Time":"2026-10-18T01:10:02.411359024Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/user\n"} +{"Time":"2026-10-18T01:10:02.435939087Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/user (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.435988041Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.435998772Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external"} +{"Time":"2026-10-18T01:10:02.436004624Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/external\n"} +{"Time":"2026-10-18T01:10:02.459357759Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/external (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.459418995Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.459434964Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion (0.05s)\n"} +{"Time":"2026-10-18T01:10:02.459444117Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Elapsed":0.05} +{"Time":"2026-10-18T01:10:02.459465876Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse"} +{"Time":"2026-10-18T01:10:02.459473946Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"=== RUN TestSourceOperationPreparedCancellationAndIDReuse\n"} +{"Time":"2026-10-18T01:10:02.483978357Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"--- PASS: TestSourceOperationPreparedCancellationAndIDReuse (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.484030964Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.4840455Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime"} +{"Time":"2026-10-18T01:10:02.484053128Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"=== RUN TestSourceOperationRejectsUnsafeRuntime\n"} +{"Time":"2026-10-18T01:10:02.513315243Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"--- PASS: TestSourceOperationRejectsUnsafeRuntime (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.513387859Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.513406452Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource"} +{"Time":"2026-10-18T01:10:02.51344299Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"=== RUN TestSourceOperationRefusesSharedManagedSource\n"} +{"Time":"2026-10-18T01:10:02.545933826Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"--- PASS: TestSourceOperationRefusesSharedManagedSource (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.546001239Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.546024017Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody"} +{"Time":"2026-10-18T01:10:02.546033827Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"=== RUN TestSourceOperationBoundsPrivateJournalCustody\n"} +{"Time":"2026-10-18T01:10:02.574522351Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"--- PASS: TestSourceOperationBoundsPrivateJournalCustody (0.03s)\n"} +{"Time":"2026-10-18T01:10:02.574585716Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Elapsed":0.03} +{"Time":"2026-10-18T01:10:02.57461164Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState"} +{"Time":"2026-10-18T01:10:02.57462073Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"=== RUN TestSourceOperationRequiresExactPostState\n"} +{"Time":"2026-10-18T01:10:02.599179436Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"--- PASS: TestSourceOperationRequiresExactPostState (0.02s)\n"} +{"Time":"2026-10-18T01:10:02.599231412Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Elapsed":0.02} +{"Time":"2026-10-18T01:10:02.599583593Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:10:02.639359789Z","Action":"output","Package":"forgejo.org/models/auth","Output":"ok \tforgejo.org/models/auth\t3.529s\n"} +{"Time":"2026-10-18T01:10:02.639410908Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":3.5300000000000002} +{"Time":"2026-10-18T01:10:11.401985609Z","Action":"start","Package":"forgejo.org/models/db"} +{"Time":"2026-10-18T01:10:11.729344921Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729432857Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/attachments\n"} +{"Time":"2026-10-18T01:10:11.729449864Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729463723Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/avatars\n"} +{"Time":"2026-10-18T01:10:11.729474679Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729484869Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/repo-avatars\n"} +{"Time":"2026-10-18T01:10:11.729495274Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729544283Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/repo-archive\n"} +{"Time":"2026-10-18T01:10:11.729555573Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729572724Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/packages\n"} +{"Time":"2026-10-18T01:10:11.729580415Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729588323Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1233407081/actions_log\n"} +{"Time":"2026-10-18T01:10:11.729597954Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:10:11.729604663Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:10:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2012268276/b1335/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:10:11.912112395Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine"} +{"Time":"2026-10-18T01:10:11.912176264Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine\n"} +{"Time":"2026-10-18T01:10:11.912338076Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine"} +{"Time":"2026-10-18T01:10:11.912371206Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/engine\n"} +{"Time":"2026-10-18T01:10:11.912418663Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/engine (0.00s)\n"} +{"Time":"2026-10-18T01:10:11.912435138Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Elapsed":0} +{"Time":"2026-10-18T01:10:11.912456446Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session"} +{"Time":"2026-10-18T01:10:11.912469412Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/session\n"} +{"Time":"2026-10-18T01:10:11.912483867Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/session (0.00s)\n"} +{"Time":"2026-10-18T01:10:11.912498295Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Elapsed":0} +{"Time":"2026-10-18T01:10:11.912528692Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine (0.00s)\n"} +{"Time":"2026-10-18T01:10:11.912541604Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Elapsed":0} +{"Time":"2026-10-18T01:10:11.91257248Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks"} +{"Time":"2026-10-18T01:10:11.912587042Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"=== RUN TestSensitiveSQLHooks\n"} +{"Time":"2026-10-18T01:10:11.917359519Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"--- PASS: TestSensitiveSQLHooks (0.00s)\n"} +{"Time":"2026-10-18T01:10:11.917418375Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Elapsed":0} +{"Time":"2026-10-18T01:10:11.91743191Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation"} +{"Time":"2026-10-18T01:10:11.917439484Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"=== RUN TestSensitiveSQLActualContextPropagation\n"} +{"Time":"2026-10-18T01:10:11.925345301Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"--- PASS: TestSensitiveSQLActualContextPropagation (0.01s)\n"} +{"Time":"2026-10-18T01:10:11.925393155Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Elapsed":0.01} +{"Time":"2026-10-18T01:10:11.925412434Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks"} +{"Time":"2026-10-18T01:10:11.925428413Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"=== RUN TestTracingContextSurvivesFollowingHooks\n"} +{"Time":"2026-10-18T01:10:11.940422188Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"--- PASS: TestTracingContextSurvivesFollowingHooks (0.01s)\n"} +{"Time":"2026-10-18T01:10:11.940475534Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Elapsed":0.01} +{"Time":"2026-10-18T01:10:11.941079952Z","Action":"output","Package":"forgejo.org/models/db","Output":"PASS\n"} +{"Time":"2026-10-18T01:10:11.9794297Z","Action":"output","Package":"forgejo.org/models/db","Output":"ok \tforgejo.org/models/db\t0.577s\n"} +{"Time":"2026-10-18T01:10:11.979489755Z","Action":"pass","Package":"forgejo.org/models/db","Elapsed":0.578} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.json new file mode 100644 index 0000000..0c0c499 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.json @@ -0,0 +1,195 @@ +{ + "action": "combined-regressions", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 23.63, + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-release-r4", + "/work", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--chdir", + "/work/source", + "--clearenv", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/db", + "/work/source/models/db", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/descriptor-fixtures-attempt2", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/descriptor-fixtures-attempt2", + "/run/writable-input", + "--", + "go", + "test", + "-p", + "1", + "-json", + "-tags", + "sqlite sqlite_unlock_notify", + "./services/auth/source/oauth2", + "./cmd", + "./models/auth", + "./models/db", + "-run", + "TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQL|TestTracingContext", + "-count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.log new file mode 100644 index 0000000..3ce7f33 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-combined-regressions-attempt2.log @@ -0,0 +1,436 @@ +{"Time":"2026-10-18T01:12:23.107872463Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"} +{"Time":"2026-10-18T01:12:23.194015099Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"} +{"Time":"2026-10-18T01:12:23.19406675Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"} +{"Time":"2026-10-18T01:12:23.194110556Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"} +{"Time":"2026-10-18T01:12:23.194116624Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"} +{"Time":"2026-10-18T01:12:23.196052187Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.196077408Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0} +{"Time":"2026-10-18T01:12:23.196089725Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"} +{"Time":"2026-10-18T01:12:23.196096254Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"} +{"Time":"2026-10-18T01:12:23.196376546Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.196396411Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0} +{"Time":"2026-10-18T01:12:23.196404689Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"} +{"Time":"2026-10-18T01:12:23.196410586Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"} +{"Time":"2026-10-18T01:12:23.196774288Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.196816822Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0} +{"Time":"2026-10-18T01:12:23.196836723Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"} +{"Time":"2026-10-18T01:12:23.196843928Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"} +{"Time":"2026-10-18T01:12:23.197004949Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.197022163Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0} +{"Time":"2026-10-18T01:12:23.197030765Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"} +{"Time":"2026-10-18T01:12:23.197054923Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"} +{"Time":"2026-10-18T01:12:23.197302554Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.197319191Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0} +{"Time":"2026-10-18T01:12:23.197326603Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"} +{"Time":"2026-10-18T01:12:23.197332329Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"} +{"Time":"2026-10-18T01:12:23.197575957Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.197600808Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0} +{"Time":"2026-10-18T01:12:23.197608987Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"} +{"Time":"2026-10-18T01:12:23.197618356Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"} +{"Time":"2026-10-18T01:12:23.197878648Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.197900506Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0} +{"Time":"2026-10-18T01:12:23.197908811Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"} +{"Time":"2026-10-18T01:12:23.197914672Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"} +{"Time":"2026-10-18T01:12:23.19813023Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.198145207Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0} +{"Time":"2026-10-18T01:12:23.198151979Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"} +{"Time":"2026-10-18T01:12:23.198157236Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"} +{"Time":"2026-10-18T01:12:23.198409449Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.198423912Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0} +{"Time":"2026-10-18T01:12:23.198442632Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"} +{"Time":"2026-10-18T01:12:23.198449215Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"} +{"Time":"2026-10-18T01:12:23.198727071Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.198752572Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0} +{"Time":"2026-10-18T01:12:23.198760669Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"} +{"Time":"2026-10-18T01:12:23.19876892Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"} +{"Time":"2026-10-18T01:12:23.198998807Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.199019375Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0} +{"Time":"2026-10-18T01:12:23.199026616Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"} +{"Time":"2026-10-18T01:12:23.199031666Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"} +{"Time":"2026-10-18T01:12:23.199276998Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.19929148Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0} +{"Time":"2026-10-18T01:12:23.199298108Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"} +{"Time":"2026-10-18T01:12:23.199303193Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"} +{"Time":"2026-10-18T01:12:23.199575713Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.199594838Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0} +{"Time":"2026-10-18T01:12:23.199607282Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"} +{"Time":"2026-10-18T01:12:23.199615073Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"} +{"Time":"2026-10-18T01:12:23.199887773Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.199911028Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0} +{"Time":"2026-10-18T01:12:23.200351214Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.200373108Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.200381278Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"} +{"Time":"2026-10-18T01:12:23.200386318Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"} +{"Time":"2026-10-18T01:12:23.200841257Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.200867776Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0} +{"Time":"2026-10-18T01:12:23.200878047Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"} +{"Time":"2026-10-18T01:12:23.200883313Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"} +{"Time":"2026-10-18T01:12:23.20111869Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.20113392Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0} +{"Time":"2026-10-18T01:12:23.201143208Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"} +{"Time":"2026-10-18T01:12:23.2011487Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"} +{"Time":"2026-10-18T01:12:23.201157158Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"} +{"Time":"2026-10-18T01:12:23.20116218Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"} +{"Time":"2026-10-18T01:12:23.201484069Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.201498776Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0} +{"Time":"2026-10-18T01:12:23.201505855Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"} +{"Time":"2026-10-18T01:12:23.201525855Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"} +{"Time":"2026-10-18T01:12:23.201769979Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.201785065Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0} +{"Time":"2026-10-18T01:12:23.201791864Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"} +{"Time":"2026-10-18T01:12:23.201797031Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"} +{"Time":"2026-10-18T01:12:23.202041516Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.202060344Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0} +{"Time":"2026-10-18T01:12:23.202067368Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"} +{"Time":"2026-10-18T01:12:23.202072634Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"} +{"Time":"2026-10-18T01:12:23.202307993Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.202323963Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0} +{"Time":"2026-10-18T01:12:23.202331402Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.202347191Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0} +{"Time":"2026-10-18T01:12:23.202353089Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"} +{"Time":"2026-10-18T01:12:23.202357958Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"} +{"Time":"2026-10-18T01:12:23.202770208Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.202785664Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0} +{"Time":"2026-10-18T01:12:23.202792202Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"} +{"Time":"2026-10-18T01:12:23.202811381Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"} +{"Time":"2026-10-18T01:12:23.202818829Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"} +{"Time":"2026-10-18T01:12:23.202824123Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"} +{"Time":"2026-10-18T01:12:23.220543576Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.02s)\n"} +{"Time":"2026-10-18T01:12:23.220595227Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.02} +{"Time":"2026-10-18T01:12:23.220621332Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"} +{"Time":"2026-10-18T01:12:23.220632126Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"} +{"Time":"2026-10-18T01:12:23.227940064Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.227989876Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.22799947Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"} +{"Time":"2026-10-18T01:12:23.228004718Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"} +{"Time":"2026-10-18T01:12:23.236625784Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.236704316Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.236715362Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (0.03s)\n"} +{"Time":"2026-10-18T01:12:23.236750809Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":0.03} +{"Time":"2026-10-18T01:12:23.236762423Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"} +{"Time":"2026-10-18T01:12:23.2367663Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"} +{"Time":"2026-10-18T01:12:23.236964879Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"} +{"Time":"2026-10-18T01:12:23.236984086Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"} +{"Time":"2026-10-18T01:12:23.246816005Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.246859153Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.246867079Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"} +{"Time":"2026-10-18T01:12:23.246870361Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"} +{"Time":"2026-10-18T01:12:23.253818143Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.253866331Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.253875962Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"} +{"Time":"2026-10-18T01:12:23.253881859Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"} +{"Time":"2026-10-18T01:12:23.261820508Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.261865577Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.261873764Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"} +{"Time":"2026-10-18T01:12:23.26187876Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"} +{"Time":"2026-10-18T01:12:23.268416158Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.268461632Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.268469712Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"} +{"Time":"2026-10-18T01:12:23.268474193Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"} +{"Time":"2026-10-18T01:12:23.275666859Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.275723866Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.275739249Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"} +{"Time":"2026-10-18T01:12:23.275744326Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"} +{"Time":"2026-10-18T01:12:23.282539083Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.282593385Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.282605684Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (0.05s)\n"} +{"Time":"2026-10-18T01:12:23.282613187Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":0.05} +{"Time":"2026-10-18T01:12:23.282623331Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"} +{"Time":"2026-10-18T01:12:23.282629715Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"} +{"Time":"2026-10-18T01:12:23.282636262Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"} +{"Time":"2026-10-18T01:12:23.282641708Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"} +{"Time":"2026-10-18T01:12:23.283829622Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.283870326Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0} +{"Time":"2026-10-18T01:12:23.283878938Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"} +{"Time":"2026-10-18T01:12:23.283885701Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"} +{"Time":"2026-10-18T01:12:23.284814353Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.284849511Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0} +{"Time":"2026-10-18T01:12:23.284868312Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"} +{"Time":"2026-10-18T01:12:23.284873777Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"} +{"Time":"2026-10-18T01:12:23.285815798Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.285856898Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0} +{"Time":"2026-10-18T01:12:23.285865438Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"} +{"Time":"2026-10-18T01:12:23.285871804Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"} +{"Time":"2026-10-18T01:12:23.286694323Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.286719373Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0} +{"Time":"2026-10-18T01:12:23.28672821Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"} +{"Time":"2026-10-18T01:12:23.28673307Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"} +{"Time":"2026-10-18T01:12:23.289408854Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.289466276Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0} +{"Time":"2026-10-18T01:12:23.289476502Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"} +{"Time":"2026-10-18T01:12:23.289488008Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"} +{"Time":"2026-10-18T01:12:23.29083514Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.290876817Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0} +{"Time":"2026-10-18T01:12:23.290886015Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"} +{"Time":"2026-10-18T01:12:23.290909415Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"} +{"Time":"2026-10-18T01:12:23.293218725Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.293265985Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0} +{"Time":"2026-10-18T01:12:23.293272757Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"} +{"Time":"2026-10-18T01:12:23.293277446Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"} +{"Time":"2026-10-18T01:12:23.294761333Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.294807574Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0} +{"Time":"2026-10-18T01:12:23.294814697Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"} +{"Time":"2026-10-18T01:12:23.294831298Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"} +{"Time":"2026-10-18T01:12:23.296016273Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.296051152Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0} +{"Time":"2026-10-18T01:12:23.296057906Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"} +{"Time":"2026-10-18T01:12:23.296061999Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"} +{"Time":"2026-10-18T01:12:23.29729968Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.297345776Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0} +{"Time":"2026-10-18T01:12:23.297353964Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.297364965Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.297408518Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"} +{"Time":"2026-10-18T01:12:23.297415011Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"} +{"Time":"2026-10-18T01:12:23.297807908Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"} +{"Time":"2026-10-18T01:12:23.297880902Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"} +{"Time":"2026-10-18T01:12:23.298159862Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.298178014Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0} +{"Time":"2026-10-18T01:12:23.298186815Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"} +{"Time":"2026-10-18T01:12:23.298191847Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"} +{"Time":"2026-10-18T01:12:23.298457278Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.298469416Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0} +{"Time":"2026-10-18T01:12:23.298479046Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"} +{"Time":"2026-10-18T01:12:23.298483356Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"} +{"Time":"2026-10-18T01:12:23.298761502Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.298774316Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0} +{"Time":"2026-10-18T01:12:23.298782026Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"} +{"Time":"2026-10-18T01:12:23.298786029Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"} +{"Time":"2026-10-18T01:12:23.299056727Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.299068062Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0} +{"Time":"2026-10-18T01:12:23.299073075Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"} +{"Time":"2026-10-18T01:12:23.299099027Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"} +{"Time":"2026-10-18T01:12:23.299321753Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.299334107Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0} +{"Time":"2026-10-18T01:12:23.299340743Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"} +{"Time":"2026-10-18T01:12:23.299344927Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"} +{"Time":"2026-10-18T01:12:23.299641919Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.299654012Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0} +{"Time":"2026-10-18T01:12:23.299660784Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"} +{"Time":"2026-10-18T01:12:23.299665049Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"} +{"Time":"2026-10-18T01:12:23.29995507Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.299967704Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0} +{"Time":"2026-10-18T01:12:23.299973159Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"} +{"Time":"2026-10-18T01:12:23.299979606Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"} +{"Time":"2026-10-18T01:12:23.300288276Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.30031145Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0} +{"Time":"2026-10-18T01:12:23.300319331Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"} +{"Time":"2026-10-18T01:12:23.300323398Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"} +{"Time":"2026-10-18T01:12:23.3005101Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.300524122Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0} +{"Time":"2026-10-18T01:12:23.300529262Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"} +{"Time":"2026-10-18T01:12:23.300544149Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"} +{"Time":"2026-10-18T01:12:23.300857362Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.300898444Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0} +{"Time":"2026-10-18T01:12:23.300908787Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"} +{"Time":"2026-10-18T01:12:23.300913314Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"} +{"Time":"2026-10-18T01:12:23.301148213Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.301164877Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0} +{"Time":"2026-10-18T01:12:23.301173552Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"} +{"Time":"2026-10-18T01:12:23.301178286Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"} +{"Time":"2026-10-18T01:12:23.301413681Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.301425322Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0} +{"Time":"2026-10-18T01:12:23.301430435Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"} +{"Time":"2026-10-18T01:12:23.301434439Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"} +{"Time":"2026-10-18T01:12:23.301723721Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.301734785Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0} +{"Time":"2026-10-18T01:12:23.301747644Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"} +{"Time":"2026-10-18T01:12:23.301751828Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"} +{"Time":"2026-10-18T01:12:23.301942002Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.301966673Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0} +{"Time":"2026-10-18T01:12:23.30197404Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"} +{"Time":"2026-10-18T01:12:23.301978107Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"} +{"Time":"2026-10-18T01:12:23.302155665Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.302167216Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0} +{"Time":"2026-10-18T01:12:23.302172392Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"} +{"Time":"2026-10-18T01:12:23.30217636Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"} +{"Time":"2026-10-18T01:12:23.302340447Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.302353305Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0} +{"Time":"2026-10-18T01:12:23.302359239Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"} +{"Time":"2026-10-18T01:12:23.302363215Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"} +{"Time":"2026-10-18T01:12:23.302435236Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.302447986Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0} +{"Time":"2026-10-18T01:12:23.302453622Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"} +{"Time":"2026-10-18T01:12:23.302458266Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01} +{"Time":"2026-10-18T01:12:23.302603597Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"} +{"Time":"2026-10-18T01:12:23.302614634Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"} +{"Time":"2026-10-18T01:12:23.302648449Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.302712868Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0} +{"Time":"2026-10-18T01:12:23.3027334Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"} +{"Time":"2026-10-18T01:12:23.302737774Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"} +{"Time":"2026-10-18T01:12:23.302766268Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.302772391Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0} +{"Time":"2026-10-18T01:12:23.302865756Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"} +{"Time":"2026-10-18T01:12:23.302876739Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"} +{"Time":"2026-10-18T01:12:23.30714995Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.307206822Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0} +{"Time":"2026-10-18T01:12:23.307223846Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"} +{"Time":"2026-10-18T01:12:23.307229915Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"} +{"Time":"2026-10-18T01:12:23.307251024Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.307378799Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0} +{"Time":"2026-10-18T01:12:23.307393893Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"} +{"Time":"2026-10-18T01:12:23.307400151Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"} +{"Time":"2026-10-18T01:12:23.307416085Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"} +{"Time":"2026-10-18T01:12:23.307593817Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0} +{"Time":"2026-10-18T01:12:23.307628019Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"} +{"Time":"2026-10-18T01:12:23.314297011Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t0.206s\n"} +{"Time":"2026-10-18T01:12:23.314394623Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":0.207} +{"Time":"2026-10-18T01:12:28.829932076Z","Action":"start","Package":"forgejo.org/cmd"} +{"Time":"2026-10-18T01:12:29.068524071Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.06863574Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/attachments\n"} +{"Time":"2026-10-18T01:12:29.068678085Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.06868612Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/avatars\n"} +{"Time":"2026-10-18T01:12:29.068692919Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.068699438Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/repo-avatars\n"} +{"Time":"2026-10-18T01:12:29.068706454Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.068712162Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/repo-archive\n"} +{"Time":"2026-10-18T01:12:29.068732495Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.068740151Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/packages\n"} +{"Time":"2026-10-18T01:12:29.068745868Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.068752442Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata843753395/actions_log\n"} +{"Time":"2026-10-18T01:12:29.068758222Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:12:29.068764245Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 01:12:29 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build4098959969/b1034/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:12:29.455916688Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata"} +{"Time":"2026-10-18T01:12:29.455979557Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"=== RUN TestPrivateSecretMetadata\n"} +{"Time":"2026-10-18T01:12:29.455996212Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"--- PASS: TestPrivateSecretMetadata (0.00s)\n"} +{"Time":"2026-10-18T01:12:29.45601237Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Elapsed":0} +{"Time":"2026-10-18T01:12:29.456025175Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement"} +{"Time":"2026-10-18T01:12:29.456030288Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"=== RUN TestPrivateSecretOpenedFileReplacement\n"} +{"Time":"2026-10-18T01:12:29.456276964Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"--- PASS: TestPrivateSecretOpenedFileReplacement (0.00s)\n"} +{"Time":"2026-10-18T01:12:29.456321383Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Elapsed":0} +{"Time":"2026-10-18T01:12:29.45696085Z","Action":"output","Package":"forgejo.org/cmd","Output":"PASS\n"} +{"Time":"2026-10-18T01:12:29.487616123Z","Action":"output","Package":"forgejo.org/cmd","Output":"ok \tforgejo.org/cmd\t0.658s\n"} +{"Time":"2026-10-18T01:12:29.487693518Z","Action":"pass","Package":"forgejo.org/cmd","Elapsed":0.658} +{"Time":"2026-10-18T01:12:32.24391159Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:12:32.437103083Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.437286268Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/attachments\n"} +{"Time":"2026-10-18T01:12:32.439621655Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439680141Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/avatars\n"} +{"Time":"2026-10-18T01:12:32.439689059Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439694514Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/repo-avatars\n"} +{"Time":"2026-10-18T01:12:32.439700267Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439705759Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/repo-archive\n"} +{"Time":"2026-10-18T01:12:32.43971024Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439714812Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/packages\n"} +{"Time":"2026-10-18T01:12:32.439719411Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439725146Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata55143680/actions_log\n"} +{"Time":"2026-10-18T01:12:32.439730655Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:12:32.439736327Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:12:32 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build4098959969/b1314/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:12:32.550268519Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion"} +{"Time":"2026-10-18T01:12:32.550394769Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"=== RUN TestSourceOperationDatabaseWriterExclusion\n"} +{"Time":"2026-10-18T01:12:32.651494782Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"--- PASS: TestSourceOperationDatabaseWriterExclusion (0.10s)\n"} +{"Time":"2026-10-18T01:12:32.65155059Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Elapsed":0.1} +{"Time":"2026-10-18T01:12:32.651902326Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging"} +{"Time":"2026-10-18T01:12:32.651919314Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"=== RUN TestSourceOperationRefusesActualXORMArgumentLogging\n"} +{"Time":"2026-10-18T01:12:32.652187071Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 01:12:32.652031 [SQL] BEGIN TRANSACTION [] - 85.456µs\n"} +{"Time":"2026-10-18T01:12:32.652216693Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 01:12:32.652121 [SQL] ROLLBACK [] - 31.786µs\n"} +{"Time":"2026-10-18T01:12:32.652227982Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"--- PASS: TestSourceOperationRefusesActualXORMArgumentLogging (0.00s)\n"} +{"Time":"2026-10-18T01:12:32.652237306Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Elapsed":0} +{"Time":"2026-10-18T01:12:32.654015671Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery"} +{"Time":"2026-10-18T01:12:32.654070009Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"=== RUN TestSourceOperationProcessRecovery\n"} +{"Time":"2026-10-18T01:12:34.27579293Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"--- PASS: TestSourceOperationProcessRecovery (1.62s)\n"} +{"Time":"2026-10-18T01:12:34.275865636Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Elapsed":1.62} +{"Time":"2026-10-18T01:12:34.27589184Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse"} +{"Time":"2026-10-18T01:12:34.275900317Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"=== RUN TestSourceOperationCreateAndInverse\n"} +{"Time":"2026-10-18T01:12:34.299633307Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"--- PASS: TestSourceOperationCreateAndInverse (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.299688502Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.299700441Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting"} +{"Time":"2026-10-18T01:12:34.299706599Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"=== RUN TestSourceOperationRestoresExactRawPreexisting\n"} +{"Time":"2026-10-18T01:12:34.321309202Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"--- PASS: TestSourceOperationRestoresExactRawPreexisting (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.321366452Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.321393901Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting"} +{"Time":"2026-10-18T01:12:34.321400998Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"=== RUN TestSourceOperationUnchangedNeverDeletesPreexisting\n"} +{"Time":"2026-10-18T01:12:34.337138315Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"--- PASS: TestSourceOperationUnchangedNeverDeletesPreexisting (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.337196666Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.337208966Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses"} +{"Time":"2026-10-18T01:12:34.337217938Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"=== RUN TestSourceOperationPrivateDriftRefuses\n"} +{"Time":"2026-10-18T01:12:34.337233754Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply"} +{"Time":"2026-10-18T01:12:34.337251906Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-apply\n"} +{"Time":"2026-10-18T01:12:34.360907139Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-apply (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.360966698Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.360980548Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse"} +{"Time":"2026-10-18T01:12:34.360986058Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-inverse\n"} +{"Time":"2026-10-18T01:12:34.377005905Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-inverse (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.377080694Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.377093986Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses (0.04s)\n"} +{"Time":"2026-10-18T01:12:34.377102254Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Elapsed":0.04} +{"Time":"2026-10-18T01:12:34.377114635Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses"} +{"Time":"2026-10-18T01:12:34.377218505Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"=== RUN TestSourceOperationStockCRUDDriftRefuses\n"} +{"Time":"2026-10-18T01:12:34.392712651Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"--- PASS: TestSourceOperationStockCRUDDriftRefuses (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.392770307Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.392781948Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion"} +{"Time":"2026-10-18T01:12:34.392830082Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion\n"} +{"Time":"2026-10-18T01:12:34.392863356Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user"} +{"Time":"2026-10-18T01:12:34.392873969Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/user\n"} +{"Time":"2026-10-18T01:12:34.410463384Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/user (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.410515693Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.410528669Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external"} +{"Time":"2026-10-18T01:12:34.410535035Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/external\n"} +{"Time":"2026-10-18T01:12:34.425620514Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/external (0.01s)\n"} +{"Time":"2026-10-18T01:12:34.425682535Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Elapsed":0.01} +{"Time":"2026-10-18T01:12:34.425696755Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion (0.03s)\n"} +{"Time":"2026-10-18T01:12:34.425705105Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Elapsed":0.03} +{"Time":"2026-10-18T01:12:34.425713049Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse"} +{"Time":"2026-10-18T01:12:34.425720651Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"=== RUN TestSourceOperationPreparedCancellationAndIDReuse\n"} +{"Time":"2026-10-18T01:12:34.440609818Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"--- PASS: TestSourceOperationPreparedCancellationAndIDReuse (0.01s)\n"} +{"Time":"2026-10-18T01:12:34.440703877Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Elapsed":0.01} +{"Time":"2026-10-18T01:12:34.44072752Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime"} +{"Time":"2026-10-18T01:12:34.440745103Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"=== RUN TestSourceOperationRejectsUnsafeRuntime\n"} +{"Time":"2026-10-18T01:12:34.454490186Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"--- PASS: TestSourceOperationRejectsUnsafeRuntime (0.01s)\n"} +{"Time":"2026-10-18T01:12:34.454544821Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Elapsed":0.01} +{"Time":"2026-10-18T01:12:34.454556174Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource"} +{"Time":"2026-10-18T01:12:34.454562053Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"=== RUN TestSourceOperationRefusesSharedManagedSource\n"} +{"Time":"2026-10-18T01:12:34.471808411Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"--- PASS: TestSourceOperationRefusesSharedManagedSource (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.47190724Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.471921632Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody"} +{"Time":"2026-10-18T01:12:34.471929522Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"=== RUN TestSourceOperationBoundsPrivateJournalCustody\n"} +{"Time":"2026-10-18T01:12:34.486704483Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"--- PASS: TestSourceOperationBoundsPrivateJournalCustody (0.01s)\n"} +{"Time":"2026-10-18T01:12:34.486759524Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Elapsed":0.01} +{"Time":"2026-10-18T01:12:34.487106372Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState"} +{"Time":"2026-10-18T01:12:34.487137292Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"=== RUN TestSourceOperationRequiresExactPostState\n"} +{"Time":"2026-10-18T01:12:34.504315067Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"--- PASS: TestSourceOperationRequiresExactPostState (0.02s)\n"} +{"Time":"2026-10-18T01:12:34.504408422Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Elapsed":0.02} +{"Time":"2026-10-18T01:12:34.504693224Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:12:34.529425421Z","Action":"output","Package":"forgejo.org/models/auth","Output":"ok \tforgejo.org/models/auth\t2.285s\n"} +{"Time":"2026-10-18T01:12:34.529471463Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":2.286} +{"Time":"2026-10-18T01:12:40.073004946Z","Action":"start","Package":"forgejo.org/models/db"} +{"Time":"2026-10-18T01:12:40.311385857Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311481791Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/attachments\n"} +{"Time":"2026-10-18T01:12:40.311560818Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311622298Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/avatars\n"} +{"Time":"2026-10-18T01:12:40.311631865Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311637411Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/repo-avatars\n"} +{"Time":"2026-10-18T01:12:40.311652912Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311665743Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/repo-archive\n"} +{"Time":"2026-10-18T01:12:40.311696357Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.31171055Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/packages\n"} +{"Time":"2026-10-18T01:12:40.311794933Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311811245Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1533321766/actions_log\n"} +{"Time":"2026-10-18T01:12:40.311818432Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:12:40.311824194Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:12:40 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build4098959969/b1335/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:12:40.457119609Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine"} +{"Time":"2026-10-18T01:12:40.457172378Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine\n"} +{"Time":"2026-10-18T01:12:40.457323702Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine"} +{"Time":"2026-10-18T01:12:40.457342151Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/engine\n"} +{"Time":"2026-10-18T01:12:40.457356227Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/engine (0.00s)\n"} +{"Time":"2026-10-18T01:12:40.457361024Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Elapsed":0} +{"Time":"2026-10-18T01:12:40.457446282Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session"} +{"Time":"2026-10-18T01:12:40.457455227Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/session\n"} +{"Time":"2026-10-18T01:12:40.457481098Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/session (0.00s)\n"} +{"Time":"2026-10-18T01:12:40.457487004Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Elapsed":0} +{"Time":"2026-10-18T01:12:40.457493668Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine (0.00s)\n"} +{"Time":"2026-10-18T01:12:40.457501251Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Elapsed":0} +{"Time":"2026-10-18T01:12:40.457650442Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks"} +{"Time":"2026-10-18T01:12:40.457664346Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"=== RUN TestSensitiveSQLHooks\n"} +{"Time":"2026-10-18T01:12:40.464198995Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"--- PASS: TestSensitiveSQLHooks (0.01s)\n"} +{"Time":"2026-10-18T01:12:40.464269663Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Elapsed":0.01} +{"Time":"2026-10-18T01:12:40.464289293Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation"} +{"Time":"2026-10-18T01:12:40.464293802Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"=== RUN TestSensitiveSQLActualContextPropagation\n"} +{"Time":"2026-10-18T01:12:40.466979632Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"--- PASS: TestSensitiveSQLActualContextPropagation (0.00s)\n"} +{"Time":"2026-10-18T01:12:40.467034394Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Elapsed":0} +{"Time":"2026-10-18T01:12:40.467046071Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks"} +{"Time":"2026-10-18T01:12:40.467051337Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"=== RUN TestTracingContextSurvivesFollowingHooks\n"} +{"Time":"2026-10-18T01:12:40.477503439Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"--- PASS: TestTracingContextSurvivesFollowingHooks (0.01s)\n"} +{"Time":"2026-10-18T01:12:40.477560338Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Elapsed":0.01} +{"Time":"2026-10-18T01:12:40.478000697Z","Action":"output","Package":"forgejo.org/models/db","Output":"PASS\n"} +{"Time":"2026-10-18T01:12:40.510473676Z","Action":"output","Package":"forgejo.org/models/db","Output":"ok \tforgejo.org/models/db\t0.437s\n"} +{"Time":"2026-10-18T01:12:40.510532243Z","Action":"pass","Package":"forgejo.org/models/db","Elapsed":0.438} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.json new file mode 100644 index 0000000..71a5d38 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.json @@ -0,0 +1,194 @@ +{ + "action": "crash", + "returncode": 73, + "expected_returncode": 73, + "outcome_matches": true, + "seconds": 2.22, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--setenv", + "CUSTODIAN_PG_STEP", + "crash", + "--", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgresProcessRecovery$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.log new file mode 100644 index 0000000..6ea36dc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-crash-attempt1.log @@ -0,0 +1,15 @@ +2026/10/18 01:02:02 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/attachments +2026/10/18 01:02:02 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/avatars +2026/10/18 01:02:02 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/repo-avatars +2026/10/18 01:02:02 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/repo-archive +2026/10/18 01:02:02 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/packages +2026/10/18 01:02:02 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4082743399/actions_log +2026/10/18 01:02:02 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local +2026/10/18 01:02:02 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts +=== RUN TestSourceOperationPostgresProcessRecovery diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.json new file mode 100644 index 0000000..a146c05 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.json @@ -0,0 +1,198 @@ +{ + "action": "db-regressions", + "returncode": 1, + "expected_returncode": 0, + "outcome_matches": false, + "seconds": 2.72, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--", + "go", + "test", + "-p", + "1", + "-json", + "-tags", + "sqlite sqlite_unlock_notify", + "./models/db", + "-run", + "TestSensitiveSQL|TestTracingContext", + "-count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.log new file mode 100644 index 0000000..ad9b570 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt1.log @@ -0,0 +1,8 @@ +{"ImportPath":"forgejo.org/models/db [forgejo.org/models/db.test]","Action":"build-output","Output":"# forgejo.org/models/db [forgejo.org/models/db.test]\n"} +{"ImportPath":"forgejo.org/models/db [forgejo.org/models/db.test]","Action":"build-output","Output":"models/db/tracing_context_test.go:27:34: too many arguments in call to engine.AddHook\n"} +{"ImportPath":"forgejo.org/models/db [forgejo.org/models/db.test]","Action":"build-output","Output":"\thave (TracingHook, followingQueryHook)\n"} +{"ImportPath":"forgejo.org/models/db [forgejo.org/models/db.test]","Action":"build-output","Output":"\twant (contexts.Hook)\n"} +{"ImportPath":"forgejo.org/models/db [forgejo.org/models/db.test]","Action":"build-fail"} +{"Time":"2026-10-18T01:00:40.417523842Z","Action":"start","Package":"forgejo.org/models/db"} +{"Time":"2026-10-18T01:00:40.417889644Z","Action":"output","Package":"forgejo.org/models/db","Output":"FAIL\tforgejo.org/models/db [build failed]\n"} +{"Time":"2026-10-18T01:00:40.417940655Z","Action":"fail","Package":"forgejo.org/models/db","Elapsed":0,"FailedBuild":"forgejo.org/models/db [forgejo.org/models/db.test]"} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.json new file mode 100644 index 0000000..9651e6b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.json @@ -0,0 +1,198 @@ +{ + "action": "db-regressions", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 80.41, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt5/db", + "/work/source/models/db", + "--", + "go", + "test", + "-p", + "1", + "-json", + "-tags", + "sqlite sqlite_unlock_notify", + "./models/db", + "-run", + "TestSensitiveSQL|TestTracingContext", + "-count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt5", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.log new file mode 100644 index 0000000..e9cee7e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-db-regressions-attempt2.log @@ -0,0 +1,42 @@ +{"Time":"2026-10-18T01:03:57.707717899Z","Action":"start","Package":"forgejo.org/models/db"} +{"Time":"2026-10-18T01:03:57.90451284Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904818516Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/attachments\n"} +{"Time":"2026-10-18T01:03:57.904834558Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904843404Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/avatars\n"} +{"Time":"2026-10-18T01:03:57.904852114Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904857885Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/repo-avatars\n"} +{"Time":"2026-10-18T01:03:57.904865379Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904871429Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/repo-archive\n"} +{"Time":"2026-10-18T01:03:57.904877182Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904888706Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/packages\n"} +{"Time":"2026-10-18T01:03:57.904907688Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904913946Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata394576552/actions_log\n"} +{"Time":"2026-10-18T01:03:57.904919554Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:03:57.904926516Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 01:03:57 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build1720325847/b001/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:03:58.002092733Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine"} +{"Time":"2026-10-18T01:03:58.00213423Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine\n"} +{"Time":"2026-10-18T01:03:58.002239173Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine"} +{"Time":"2026-10-18T01:03:58.002249867Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/engine\n"} +{"Time":"2026-10-18T01:03:58.002337939Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/engine (0.00s)\n"} +{"Time":"2026-10-18T01:03:58.002352592Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Elapsed":0} +{"Time":"2026-10-18T01:03:58.002396902Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session"} +{"Time":"2026-10-18T01:03:58.002403683Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/session\n"} +{"Time":"2026-10-18T01:03:58.002410203Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/session (0.00s)\n"} +{"Time":"2026-10-18T01:03:58.00241996Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Elapsed":0} +{"Time":"2026-10-18T01:03:58.002425776Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine (0.00s)\n"} +{"Time":"2026-10-18T01:03:58.002431222Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Elapsed":0} +{"Time":"2026-10-18T01:03:58.002436497Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks"} +{"Time":"2026-10-18T01:03:58.002441123Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"=== RUN TestSensitiveSQLHooks\n"} +{"Time":"2026-10-18T01:03:58.009355975Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"--- PASS: TestSensitiveSQLHooks (0.01s)\n"} +{"Time":"2026-10-18T01:03:58.009404442Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Elapsed":0.01} +{"Time":"2026-10-18T01:03:58.00947411Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation"} +{"Time":"2026-10-18T01:03:58.009483434Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"=== RUN TestSensitiveSQLActualContextPropagation\n"} +{"Time":"2026-10-18T01:03:58.011965962Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"--- PASS: TestSensitiveSQLActualContextPropagation (0.00s)\n"} +{"Time":"2026-10-18T01:03:58.012015268Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Elapsed":0} +{"Time":"2026-10-18T01:03:58.012025927Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks"} +{"Time":"2026-10-18T01:03:58.012031031Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"=== RUN TestTracingContextSurvivesFollowingHooks\n"} +{"Time":"2026-10-18T01:03:58.022381389Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Output":"--- PASS: TestTracingContextSurvivesFollowingHooks (0.01s)\n"} +{"Time":"2026-10-18T01:03:58.022432489Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestTracingContextSurvivesFollowingHooks","Elapsed":0.01} +{"Time":"2026-10-18T01:03:58.022458856Z","Action":"output","Package":"forgejo.org/models/db","Output":"PASS\n"} +{"Time":"2026-10-18T01:03:58.053650408Z","Action":"output","Package":"forgejo.org/models/db","Output":"ok \tforgejo.org/models/db\t0.345s\n"} +{"Time":"2026-10-18T01:03:58.053699994Z","Action":"pass","Package":"forgejo.org/models/db","Elapsed":0.346} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.json new file mode 100644 index 0000000..fc74ff2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.json @@ -0,0 +1,200 @@ +{ + "action": "prepare", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 3.93, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--setenv", + "CUSTODIAN_PG_STEP", + "prepare", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgresProcessRecovery$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.log new file mode 100644 index 0000000..0ac0ccc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-prepare-attempt1.log @@ -0,0 +1,21 @@ +{"Time":"2026-10-18T01:01:39.579840247Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:01:39.74732574Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747414668Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/attachments\n"} +{"Time":"2026-10-18T01:01:39.74742373Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747433811Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/avatars\n"} +{"Time":"2026-10-18T01:01:39.747439465Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747444542Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/repo-avatars\n"} +{"Time":"2026-10-18T01:01:39.747449853Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747454767Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/repo-archive\n"} +{"Time":"2026-10-18T01:01:39.747459646Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747464524Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/packages\n"} +{"Time":"2026-10-18T01:01:39.747469294Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.7474741Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata897151525/actions_log\n"} +{"Time":"2026-10-18T01:01:39.747478907Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:01:39.747483695Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:01:39 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:01:39.865224725Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery"} +{"Time":"2026-10-18T01:01:39.865297791Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"=== RUN TestSourceOperationPostgresProcessRecovery\n"} +{"Time":"2026-10-18T01:01:43.405402416Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"--- PASS: TestSourceOperationPostgresProcessRecovery (3.54s)\n"} +{"Time":"2026-10-18T01:01:43.405458737Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Elapsed":3.54} +{"Time":"2026-10-18T01:01:43.405692672Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:01:43.431507112Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":3.852} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.json new file mode 100644 index 0000000..afb327d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.json @@ -0,0 +1,200 @@ +{ + "action": "recover-applied", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 2.27, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--setenv", + "CUSTODIAN_PG_STEP", + "recover-applied", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgresProcessRecovery$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.log new file mode 100644 index 0000000..4061f35 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-applied-attempt1.log @@ -0,0 +1,21 @@ +{"Time":"2026-10-18T01:04:08.08440522Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:04:08.227331233Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227448196Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/attachments\n"} +{"Time":"2026-10-18T01:04:08.227457303Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227472362Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/avatars\n"} +{"Time":"2026-10-18T01:04:08.22747954Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227486167Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/repo-avatars\n"} +{"Time":"2026-10-18T01:04:08.22749266Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227499026Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/repo-archive\n"} +{"Time":"2026-10-18T01:04:08.227505455Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227511695Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/packages\n"} +{"Time":"2026-10-18T01:04:08.227528314Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.22753468Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1812313322/actions_log\n"} +{"Time":"2026-10-18T01:04:08.227543706Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:04:08.227550036Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:08 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:04:08.330506549Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery"} +{"Time":"2026-10-18T01:04:08.330564765Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"=== RUN TestSourceOperationPostgresProcessRecovery\n"} +{"Time":"2026-10-18T01:04:10.289864261Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"--- PASS: TestSourceOperationPostgresProcessRecovery (1.96s)\n"} +{"Time":"2026-10-18T01:04:10.289918455Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Elapsed":1.96} +{"Time":"2026-10-18T01:04:10.28996263Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:04:10.314327337Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":2.23} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.json new file mode 100644 index 0000000..c8334cc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.json @@ -0,0 +1,200 @@ +{ + "action": "recover-inverse", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 4.77, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--setenv", + "CUSTODIAN_PG_STEP", + "recover-inverse", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgresProcessRecovery$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.log new file mode 100644 index 0000000..61c141a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-inverse-attempt1.log @@ -0,0 +1,21 @@ +{"Time":"2026-10-18T01:04:15.038955865Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:04:15.397703648Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397820971Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/attachments\n"} +{"Time":"2026-10-18T01:04:15.397833126Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397848681Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/avatars\n"} +{"Time":"2026-10-18T01:04:15.397862757Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397871035Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/repo-avatars\n"} +{"Time":"2026-10-18T01:04:15.397877644Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397884588Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/repo-archive\n"} +{"Time":"2026-10-18T01:04:15.397891432Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397898267Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/packages\n"} +{"Time":"2026-10-18T01:04:15.39791173Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.397919268Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata153111776/actions_log\n"} +{"Time":"2026-10-18T01:04:15.398162834Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:04:15.398206793Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:04:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:04:15.582049278Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery"} +{"Time":"2026-10-18T01:04:15.582213644Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"=== RUN TestSourceOperationPostgresProcessRecovery\n"} +{"Time":"2026-10-18T01:04:19.683251057Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"--- PASS: TestSourceOperationPostgresProcessRecovery (4.10s)\n"} +{"Time":"2026-10-18T01:04:19.683317235Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Elapsed":4.1} +{"Time":"2026-10-18T01:04:19.683355432Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:04:19.717408656Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":4.678} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.json new file mode 100644 index 0000000..744b67c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.json @@ -0,0 +1,200 @@ +{ + "action": "recover-prepared", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 3.17, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--setenv", + "CUSTODIAN_PG_STEP", + "recover-prepared", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgresProcessRecovery$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.log new file mode 100644 index 0000000..0845449 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-recover-prepared-attempt1.log @@ -0,0 +1,21 @@ +{"Time":"2026-10-18T01:03:05.798825901Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:03:06.006751622Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006904645Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/attachments\n"} +{"Time":"2026-10-18T01:03:06.006913644Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006923401Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/avatars\n"} +{"Time":"2026-10-18T01:03:06.00692846Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006933067Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/repo-avatars\n"} +{"Time":"2026-10-18T01:03:06.006940002Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006945538Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/repo-archive\n"} +{"Time":"2026-10-18T01:03:06.006949975Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006954375Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/packages\n"} +{"Time":"2026-10-18T01:03:06.006965331Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006970922Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2916044918/actions_log\n"} +{"Time":"2026-10-18T01:03:06.006975304Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:03:06.006979578Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:03:06 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:03:06.127322066Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery"} +{"Time":"2026-10-18T01:03:06.127401752Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"=== RUN TestSourceOperationPostgresProcessRecovery\n"} +{"Time":"2026-10-18T01:03:08.84880067Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Output":"--- PASS: TestSourceOperationPostgresProcessRecovery (2.72s)\n"} +{"Time":"2026-10-18T01:03:08.848862213Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgresProcessRecovery","Elapsed":2.72} +{"Time":"2026-10-18T01:03:08.849116041Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:03:08.889481889Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":3.091} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.json new file mode 100644 index 0000000..6f98cb4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.json @@ -0,0 +1,163 @@ +{ + "action": "suite", + "returncode": 1, + "expected_returncode": 0, + "outcome_matches": false, + "seconds": 10.04, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:852bf5cd2d2a8504d3b82723e09f296436e546fefcf6e5c2280e76f2ef1a7f17", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:a58899fab4b0355a6d9436ab726b1aeada01a2987856b3490a15cf3d33984a88" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.log new file mode 100644 index 0000000..d893072 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt1.log @@ -0,0 +1,121 @@ +{"Time":"2026-10-18T00:54:19.636581022Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T00:54:19.842990527Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843125687Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/attachments\n"} +{"Time":"2026-10-18T00:54:19.843136949Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843150656Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/avatars\n"} +{"Time":"2026-10-18T00:54:19.843157013Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.84316144Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/repo-avatars\n"} +{"Time":"2026-10-18T00:54:19.843166535Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843172703Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/repo-archive\n"} +{"Time":"2026-10-18T00:54:19.843181927Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843186716Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/packages\n"} +{"Time":"2026-10-18T00:54:19.843192243Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843196986Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1795841647/actions_log\n"} +{"Time":"2026-10-18T00:54:19.843201675Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T00:54:19.843209529Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:54:19 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T00:54:20.426786911Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T00:54:20.426845226Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T00:54:20.426853882Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public"} +{"Time":"2026-10-18T00:54:20.426857976Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":"=== RUN TestSourceOperationPostgres/public\n"} +{"Time":"2026-10-18T00:54:23.430331356Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse"} +{"Time":"2026-10-18T00:54:23.430407065Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/public/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:54:23.55050287Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:54:23.550546062Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/public/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:54:23.652105758Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:54:23.652148581Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/public/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:54:23.75315772Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply"} +{"Time":"2026-10-18T00:54:23.75324232Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:54:23.846277143Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse"} +{"Time":"2026-10-18T00:54:23.846321454Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:54:23.946441423Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:54:23.946608458Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:24.040268154Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" source_operation_postgres_test.go:166: \n"} +{"Time":"2026-10-18T00:54:24.04029856Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:166\n"} +{"Time":"2026-10-18T00:54:24.040303456Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:77\n"} +{"Time":"2026-10-18T00:54:24.040307262Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" \tError: \tReceived unexpected error:\n"} +{"Time":"2026-10-18T00:54:24.04031095Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" \t \tpq: null value in column \"email\" of relation \"user\" violates not-null constraint\n"} +{"Time":"2026-10-18T00:54:24.040334557Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" \tTest: \tTestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:24.040342862Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:54:24.040354746Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:24.140987869Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:54:24.141025372Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:54:24.241651942Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded"} +{"Time":"2026-10-18T00:54:24.241705477Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T00:54:24.942575411Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T00:54:24.942608009Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T00:54:25.039882318Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named"} +{"Time":"2026-10-18T00:54:25.039947494Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named","Output":"=== RUN TestSourceOperationPostgres/custodian_named\n"} +{"Time":"2026-10-18T00:54:27.982470899Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/create-reconcile-inverse"} +{"Time":"2026-10-18T00:54:27.982523911Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/custodian_named/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:54:28.094540307Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:54:28.09457744Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:54:28.19606351Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:54:28.196107541Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:54:28.289418123Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-apply"} +{"Time":"2026-10-18T00:54:28.289458123Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/custodian_named/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:54:28.380916369Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-inverse"} +{"Time":"2026-10-18T00:54:28.380955756Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/custodian_named/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:54:28.475907423Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:54:28.475950958Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:28.56714897Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" source_operation_postgres_test.go:166: \n"} +{"Time":"2026-10-18T00:54:28.567212731Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:166\n"} +{"Time":"2026-10-18T00:54:28.567236915Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:77\n"} +{"Time":"2026-10-18T00:54:28.567250071Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" \tError: \tReceived unexpected error:\n"} +{"Time":"2026-10-18T00:54:28.567260333Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" \t \tpq: null value in column \"email\" of relation \"user\" violates not-null constraint\n"} +{"Time":"2026-10-18T00:54:28.567269422Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" \tTest: \tTestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:28.567281217Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:54:28.567289837Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:54:28.66315679Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:54:28.663196115Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:54:28.7561333Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded"} +{"Time":"2026-10-18T00:54:28.7561757Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T00:54:29.458442433Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T00:54:29.458498141Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T00:54:29.582930434Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- FAIL: TestSourceOperationPostgres (9.16s)\n"} +{"Time":"2026-10-18T00:54:29.582995484Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":" --- FAIL: TestSourceOperationPostgres/public (4.61s)\n"} +{"Time":"2026-10-18T00:54:29.583007333Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T00:54:29.583014484Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T00:54:29.583023276Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/public/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583030102Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583035449Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/public/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583042013Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583047144Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.583053727Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.583059354Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583066198Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583071978Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" --- FAIL: TestSourceOperationPostgres/public/linked-user-refuses-deletion (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.583078073Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.583083394Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583089877Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583095477Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583102068Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583143692Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/public/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T00:54:29.583157966Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T00:54:29.583164486Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583182214Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583188237Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Elapsed":4.61} +{"Time":"2026-10-18T00:54:29.583193837Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named","Output":" --- FAIL: TestSourceOperationPostgres/custodian_named (4.54s)\n"} +{"Time":"2026-10-18T00:54:29.583200915Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/create-reconcile-inverse (0.11s)\n"} +{"Time":"2026-10-18T00:54:29.583207083Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/create-reconcile-inverse","Elapsed":0.11} +{"Time":"2026-10-18T00:54:29.583212512Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583219058Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583224847Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.583231168Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unchanged-preexisting-survives","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.58323574Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.583241231Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.583245722Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/private-drift-before-inverse (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.5832506Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/private-drift-before-inverse","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.583258256Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Output":" --- FAIL: TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.58326336Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-user-refuses-deletion","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.583274406Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:54:29.583280069Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:54:29.583284749Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back (0.09s)\n"} +{"Time":"2026-10-18T00:54:29.583289925Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/unexpected-trigger-post-state-rolls-back","Elapsed":0.09} +{"Time":"2026-10-18T00:54:29.58329428Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T00:54:29.583299636Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T00:54:29.583304388Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner (0.12s)\n"} +{"Time":"2026-10-18T00:54:29.583312215Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named/concurrent-preparation-has-one-owner","Elapsed":0.12} +{"Time":"2026-10-18T00:54:29.583316336Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/custodian_named","Elapsed":4.54} +{"Time":"2026-10-18T00:54:29.583320845Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Elapsed":9.16} +{"Time":"2026-10-18T00:54:29.5833252Z","Action":"output","Package":"forgejo.org/models/auth","Output":"FAIL\n"} +{"Time":"2026-10-18T00:54:29.611127117Z","Action":"fail","Package":"forgejo.org/models/auth","Elapsed":9.975} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.json new file mode 100644 index 0000000..4fec03f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.json @@ -0,0 +1,163 @@ +{ + "action": "suite", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 10.59, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt2/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt2", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt2", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:0475dfa7d5e31733321134345ff90a3cd9fd8049db9c2b7d54ef1a71c0d70afd", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:9eadebf02dabd91ed50ddd590b62bda1ee4a89cd824069495603ad62b7d144ec" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.log new file mode 100644 index 0000000..638ffed --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt2.log @@ -0,0 +1,153 @@ +{"Time":"2026-10-18T00:55:55.906057855Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T00:55:56.038866329Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038935022Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/attachments\n"} +{"Time":"2026-10-18T00:55:56.038939801Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038946501Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/avatars\n"} +{"Time":"2026-10-18T00:55:56.038950126Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038953534Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/repo-avatars\n"} +{"Time":"2026-10-18T00:55:56.038956952Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.03896018Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/repo-archive\n"} +{"Time":"2026-10-18T00:55:56.038963489Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038966627Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/packages\n"} +{"Time":"2026-10-18T00:55:56.03896972Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038972786Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1794699226/actions_log\n"} +{"Time":"2026-10-18T00:55:56.038975897Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T00:55:56.038978963Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:55:56 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T00:55:56.149624607Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T00:55:56.149763103Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T00:55:56.149778577Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default"} +{"Time":"2026-10-18T00:55:56.149783256Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":"=== RUN TestSourceOperationPostgres/default\n"} +{"Time":"2026-10-18T00:55:58.004474379Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse"} +{"Time":"2026-10-18T00:55:58.004535444Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/default/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:55:58.12083247Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:55:58.120870225Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/default/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:55:58.221400056Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:55:58.221453645Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/default/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:55:58.349182906Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply"} +{"Time":"2026-10-18T00:55:58.34923728Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:55:58.439484005Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse"} +{"Time":"2026-10-18T00:55:58.439533572Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:55:58.532963858Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:55:58.533017555Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:55:58.630188413Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:55:58.630228819Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:55:58.726484208Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:55:58.726543235Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:55:58.823504177Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded"} +{"Time":"2026-10-18T00:55:58.823540435Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/default/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T00:55:59.525498454Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T00:55:59.525539464Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T00:55:59.629264816Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public"} +{"Time":"2026-10-18T00:55:59.629317089Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":"=== RUN TestSourceOperationPostgres/public\n"} +{"Time":"2026-10-18T00:56:05.002596034Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse"} +{"Time":"2026-10-18T00:56:05.002650291Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/public/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:56:05.122746115Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:56:05.122785799Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/public/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:56:05.224658278Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:56:05.224704329Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/public/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:56:05.324254052Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply"} +{"Time":"2026-10-18T00:56:05.324306658Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:56:05.421027627Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse"} +{"Time":"2026-10-18T00:56:05.421068628Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:56:05.519545031Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:56:05.519602552Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:56:05.621130135Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:56:05.621169432Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:56:05.724423108Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:56:05.724484038Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:56:05.821994558Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded"} +{"Time":"2026-10-18T00:56:05.822048968Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T00:56:06.524609212Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T00:56:06.524651467Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T00:56:06.627601789Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named"} +{"Time":"2026-10-18T00:56:06.627652556Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":"=== RUN TestSourceOperationPostgres/named\n"} +{"Time":"2026-10-18T00:56:08.37492045Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse"} +{"Time":"2026-10-18T00:56:08.374966591Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/named/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:56:08.492727684Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:56:08.492776179Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/named/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:56:08.590136893Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:56:08.590173999Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/named/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:56:08.688574718Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply"} +{"Time":"2026-10-18T00:56:08.688620842Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:56:08.778782744Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse"} +{"Time":"2026-10-18T00:56:08.778823944Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:56:08.876909706Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:56:08.876952565Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:56:08.980394453Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:56:08.980443363Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:56:09.085590182Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:56:09.085670842Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:56:09.186028161Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded"} +{"Time":"2026-10-18T00:56:09.186065916Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T00:56:09.889229469Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T00:56:09.88927167Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T00:56:09.987233158Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- PASS: TestSourceOperationPostgres (10.26s)\n"} +{"Time":"2026-10-18T00:56:09.987296784Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":" --- PASS: TestSourceOperationPostgres/default (3.48s)\n"} +{"Time":"2026-10-18T00:56:09.987307316Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T00:56:09.987314205Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T00:56:09.987405947Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/default/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987419229Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987425433Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/default/unchanged-preexisting-survives (0.13s)\n"} +{"Time":"2026-10-18T00:56:09.987435623Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Elapsed":0.13} +{"Time":"2026-10-18T00:56:09.987443901Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T00:56:09.987453657Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T00:56:09.987460447Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-inverse (0.09s)\n"} +{"Time":"2026-10-18T00:56:09.987469555Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Elapsed":0.09} +{"Time":"2026-10-18T00:56:09.987476579Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987500096Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987508067Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987519086Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987528266Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987541765Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987552225Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/default/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T00:56:09.987558068Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T00:56:09.98756612Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987631315Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987639367Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Elapsed":3.48} +{"Time":"2026-10-18T00:56:09.987647546Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":" --- PASS: TestSourceOperationPostgres/public (3.42s)\n"} +{"Time":"2026-10-18T00:56:09.987656419Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T00:56:09.987671469Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T00:56:09.987687745Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/public/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987696979Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.98770596Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/public/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987712443Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987718115Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-apply (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987738783Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987749062Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987756583Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987762083Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987768251Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987773391Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987779568Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987799487Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987811741Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987830741Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/public/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T00:56:09.987841164Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T00:56:09.987871282Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987888198Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987898686Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Elapsed":3.42} +{"Time":"2026-10-18T00:56:09.98790397Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":" --- PASS: TestSourceOperationPostgres/named (3.36s)\n"} +{"Time":"2026-10-18T00:56:09.98791424Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T00:56:09.987923636Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T00:56:09.987930652Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/named/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987949642Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987956261Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/named/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987962383Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.987967731Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T00:56:09.98797765Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T00:56:09.987983321Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.987996469Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.988002276Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.988013502Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.988022952Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-external-refuses-deletion (0.11s)\n"} +{"Time":"2026-10-18T00:56:09.98806417Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Elapsed":0.11} +{"Time":"2026-10-18T00:56:09.9880716Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.988077795Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.988083061Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/named/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T00:56:09.988088913Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T00:56:09.988094134Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T00:56:09.988099301Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T00:56:09.988104171Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Elapsed":3.36} +{"Time":"2026-10-18T00:56:09.988109319Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Elapsed":10.26} +{"Time":"2026-10-18T00:56:09.988136696Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T00:56:10.013152394Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":10.525} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.json new file mode 100644 index 0000000..91563b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.json @@ -0,0 +1,163 @@ +{ + "action": "suite", + "returncode": 1, + "expected_returncode": 0, + "outcome_matches": false, + "seconds": 3.07, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt3/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt3", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt3", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:b1da12376c4ee8f30e5b8302236fc77b30dd0b39b6c1c570134508bce15f55d8" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.log new file mode 100644 index 0000000..42e62d4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt3.log @@ -0,0 +1,89 @@ +{"Time":"2026-10-18T00:57:47.387058986Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T00:57:51.115296969Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115409856Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/attachments\n"} +{"Time":"2026-10-18T00:57:51.115418747Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115429658Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/avatars\n"} +{"Time":"2026-10-18T00:57:51.115435663Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115441272Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/repo-avatars\n"} +{"Time":"2026-10-18T00:57:51.115446791Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115452273Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/repo-archive\n"} +{"Time":"2026-10-18T00:57:51.115457873Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115463175Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/packages\n"} +{"Time":"2026-10-18T00:57:51.115468513Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115473842Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata1185331334/actions_log\n"} +{"Time":"2026-10-18T00:57:51.11547918Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T00:57:51.115484483Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 00:57:51 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T00:57:51.246161651Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T00:57:51.246210209Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T00:57:51.246217197Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default"} +{"Time":"2026-10-18T00:57:51.2462213Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":"=== RUN TestSourceOperationPostgres/default\n"} +{"Time":"2026-10-18T00:57:53.061963283Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse"} +{"Time":"2026-10-18T00:57:53.06205406Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/default/create-reconcile-inverse\n"} +{"Time":"2026-10-18T00:57:53.181630846Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T00:57:53.181671523Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/default/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T00:57:53.286383858Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives"} +{"Time":"2026-10-18T00:57:53.286422551Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/default/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T00:57:53.384345449Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply"} +{"Time":"2026-10-18T00:57:53.384386225Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-apply\n"} +{"Time":"2026-10-18T00:57:53.474131611Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse"} +{"Time":"2026-10-18T00:57:53.474176553Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-inverse\n"} +{"Time":"2026-10-18T00:57:53.568216233Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion"} +{"Time":"2026-10-18T00:57:53.568257252Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T00:57:53.665078598Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion"} +{"Time":"2026-10-18T00:57:53.665134352Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T00:57:53.761902418Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T00:57:53.761937783Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T00:57:53.862942636Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T00:57:53.862983043Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T00:57:53.945700199Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- FAIL: TestSourceOperationPostgres (2.70s)\n"} +{"Time":"2026-10-18T00:57:53.945731876Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":" --- FAIL: TestSourceOperationPostgres/default (2.70s)\n"} +{"Time":"2026-10-18T00:57:53.945747476Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T00:57:53.945752273Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T00:57:53.945758143Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/default/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T00:57:53.945762147Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T00:57:53.945765565Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/default/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T00:57:53.94576937Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T00:57:53.945772706Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T00:57:53.945776412Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T00:57:53.945779704Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-inverse (0.09s)\n"} +{"Time":"2026-10-18T00:57:53.945783743Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Elapsed":0.09} +{"Time":"2026-10-18T00:57:53.945787089Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:57:53.945790759Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:57:53.945793969Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T00:57:53.945797901Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T00:57:53.945801084Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T00:57:53.945804988Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T00:57:53.945808279Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" --- FAIL: TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics (0.08s)\n"} +{"Time":"2026-10-18T00:57:53.947939229Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"panic: interface conversion: interface {} is nil, not *trace.Task [recovered]\n"} +{"Time":"2026-10-18T00:57:53.947988932Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\tpanic: interface conversion: interface {} is nil, not *trace.Task\n"} +{"Time":"2026-10-18T00:57:53.948006543Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\n"} +{"Time":"2026-10-18T00:57:53.948014352Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"goroutine 216 [running]:\n"} +{"Time":"2026-10-18T00:57:53.948032603Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"testing.tRunner.func1.2({0x18a8180, 0xc0012bfe30})\n"} +{"Time":"2026-10-18T00:57:53.94804061Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/go/src/testing/testing.go:1734 +0x21c\n"} +{"Time":"2026-10-18T00:57:53.948047129Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"testing.tRunner.func1()\n"} +{"Time":"2026-10-18T00:57:53.948053487Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/go/src/testing/testing.go:1737 +0x35e\n"} +{"Time":"2026-10-18T00:57:53.948063559Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"panic({0x18a8180?, 0xc0012bfe30?})\n"} +{"Time":"2026-10-18T00:57:53.94807052Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/go/src/runtime/panic.go:792 +0x132\n"} +{"Time":"2026-10-18T00:57:53.94807805Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"forgejo.org/models/db.TracingHook.AfterProcess({}, 0xc0012bfe00?)\n"} +{"Time":"2026-10-18T00:57:53.948084605Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/source/models/db/engine.go:343 +0x57\n"} +{"Time":"2026-10-18T00:57:53.94809153Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm/contexts.(*Hooks).AfterProcess(0x2042d60?, 0xc00050dd80)\n"} +{"Time":"2026-10-18T00:57:53.948098221Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/contexts/hook.go:75 +0x6a\n"} +{"Time":"2026-10-18T00:57:53.948119718Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm/core.(*DB).afterProcess(0xc000f801e0, 0xc00050dd80)\n"} +{"Time":"2026-10-18T00:57:53.948126932Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/core/db.go:298 +0x38\n"} +{"Time":"2026-10-18T00:57:53.948136788Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm/core.(*DB).ExecContext(0xc000f801e0, {0x2050538, 0x32ad0c0}, {0xc001f8ce00, 0x25}, {0xc001415320, 0x1, 0x1})\n"} +{"Time":"2026-10-18T00:57:53.948158303Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/core/db.go:275 +0x22a\n"} +{"Time":"2026-10-18T00:57:53.948165814Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm.(*Session).exec(0xc0017b8c30, {0xc001f8ce00, 0x25}, {0xc001415320, 0x1, 0x1})\n"} +{"Time":"2026-10-18T00:57:53.948173164Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/session_raw.go:178 +0x22b\n"} +{"Time":"2026-10-18T00:57:53.948182397Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm.(*Session).Exec(0xc0017b8c30, {0xc000422a00?, 0x47a779?, 0x20?})\n"} +{"Time":"2026-10-18T00:57:53.948189178Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/session_raw.go:196 +0x133\n"} +{"Time":"2026-10-18T00:57:53.948198538Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"xorm.io/xorm.(*Engine).Exec(0x20505e0?, {0xc000422a00, 0x2, 0x2})\n"} +{"Time":"2026-10-18T00:57:53.948205364Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/modules/xorm.io/xorm@v1.3.9/engine.go:1180 +0x7f\n"} +{"Time":"2026-10-18T00:57:53.948216401Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"forgejo.org/models/auth_test.TestSourceOperationPostgres.func1.8(0xc001e836c0)\n"} +{"Time":"2026-10-18T00:57:53.948225328Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/source/models/auth/source_operation_postgres_test.go:230 +0x355\n"} +{"Time":"2026-10-18T00:57:53.948234418Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"forgejo.org/models/auth_test.TestSourceOperationPostgres.func1.TestSourceOperationPostgres.func1.1.17(0xc001e836c0)\n"} +{"Time":"2026-10-18T00:57:53.948243029Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/source/models/auth/source_operation_postgres_test.go:106 +0x34\n"} +{"Time":"2026-10-18T00:57:53.948253823Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"testing.tRunner(0xc001e836c0, 0xc001f83cc8)\n"} +{"Time":"2026-10-18T00:57:53.948264247Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/go/src/testing/testing.go:1792 +0xf4\n"} +{"Time":"2026-10-18T00:57:53.948278016Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"created by testing.(*T).Run in goroutine 75\n"} +{"Time":"2026-10-18T00:57:53.948286537Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\t/work/go/src/testing/testing.go:1851 +0x413\n"} +{"Time":"2026-10-18T00:57:53.955018288Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Elapsed":2.9859999999999998} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.json new file mode 100644 index 0000000..122a88a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.json @@ -0,0 +1,197 @@ +{ + "action": "suite", + "returncode": 0, + "expected_returncode": 0, + "outcome_matches": true, + "seconds": 11.5, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-attempt4/db", + "/work/source/models/db", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-attempt4", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:53d35e31ae9f318d8f77a323fdc5b18bae804bdc86ccb639f335dfbe59e9b17c" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "none" + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.log new file mode 100644 index 0000000..3f06ba3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt4.log @@ -0,0 +1,165 @@ +{"Time":"2026-10-18T01:00:36.620730123Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:00:36.784427451Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.784508408Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/attachments\n"} +{"Time":"2026-10-18T01:00:36.784514847Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.784523846Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/avatars\n"} +{"Time":"2026-10-18T01:00:36.784529067Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.784534207Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/repo-avatars\n"} +{"Time":"2026-10-18T01:00:36.784538995Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.78454372Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/repo-archive\n"} +{"Time":"2026-10-18T01:00:36.784548436Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.784553089Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/packages\n"} +{"Time":"2026-10-18T01:00:36.784557687Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.78456225Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2682928195/actions_log\n"} +{"Time":"2026-10-18T01:00:36.784566867Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:00:36.784571475Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:00:36 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:00:36.903132953Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T01:00:36.903208733Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T01:00:36.903228752Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default"} +{"Time":"2026-10-18T01:00:36.903366463Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":"=== RUN TestSourceOperationPostgres/default\n"} +{"Time":"2026-10-18T01:00:39.109392342Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse"} +{"Time":"2026-10-18T01:00:39.109536789Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/default/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:00:39.256485133Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:00:39.25654031Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/default/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:00:39.392573659Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:00:39.392629656Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/default/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:00:39.510266053Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply"} +{"Time":"2026-10-18T01:00:39.510317848Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:00:39.623787643Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse"} +{"Time":"2026-10-18T01:00:39.623827409Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:00:39.736117733Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:00:39.736163586Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:39.84999188Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:00:39.850032557Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:39.969798121Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:00:39.969845904Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:00:40.085454914Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:00:40.085499351Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:00:40.205181631Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:00:40.205225798Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/default/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:00:40.921813572Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:00:40.921863446Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:00:41.045454997Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public"} +{"Time":"2026-10-18T01:00:41.045542509Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":"=== RUN TestSourceOperationPostgres/public\n"} +{"Time":"2026-10-18T01:00:42.870327539Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse"} +{"Time":"2026-10-18T01:00:42.870372697Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/public/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:00:43.001004999Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:00:43.00104508Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/public/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:00:43.105088518Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:00:43.105147166Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/public/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:00:43.209857825Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply"} +{"Time":"2026-10-18T01:00:43.209898402Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:00:43.300085634Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse"} +{"Time":"2026-10-18T01:00:43.30013036Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:00:43.399606765Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:00:43.399652473Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:43.499626237Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:00:43.499669736Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:43.59826031Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:00:43.59834371Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:00:43.69218378Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:00:43.692234718Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:00:43.794643228Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:00:43.794685663Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:00:44.495231495Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:00:44.495267474Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:00:44.596410773Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named"} +{"Time":"2026-10-18T01:00:44.596500062Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":"=== RUN TestSourceOperationPostgres/named\n"} +{"Time":"2026-10-18T01:00:46.266037797Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse"} +{"Time":"2026-10-18T01:00:46.266073334Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/named/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:00:46.38746905Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:00:46.387518347Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/named/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:00:46.493663624Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:00:46.493727539Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/named/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:00:46.597182621Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply"} +{"Time":"2026-10-18T01:00:46.597235336Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:00:50.271070695Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse"} +{"Time":"2026-10-18T01:00:50.271117413Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:00:50.367624615Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:00:50.367690522Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:50.478160697Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:00:50.478209977Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:00:50.574002056Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:00:50.574044897Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:00:50.67009112Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:00:50.670144881Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:00:50.773203358Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:00:50.773243368Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:00:51.478348048Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:00:51.478411989Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:00:51.582072541Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- PASS: TestSourceOperationPostgres (11.10s)\n"} +{"Time":"2026-10-18T01:00:51.582145924Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":" --- PASS: TestSourceOperationPostgres/default (4.14s)\n"} +{"Time":"2026-10-18T01:00:51.582159422Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/create-reconcile-inverse (0.15s)\n"} +{"Time":"2026-10-18T01:00:51.58216981Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Elapsed":0.15} +{"Time":"2026-10-18T01:00:51.582408966Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/default/exact-raw-preexisting-restoration (0.14s)\n"} +{"Time":"2026-10-18T01:00:51.582431256Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Elapsed":0.14} +{"Time":"2026-10-18T01:00:51.582438984Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/default/unchanged-preexisting-survives (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582466721Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582477172Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-apply (0.11s)\n"} +{"Time":"2026-10-18T01:00:51.582486126Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Elapsed":0.11} +{"Time":"2026-10-18T01:00:51.582491329Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-inverse (0.11s)\n"} +{"Time":"2026-10-18T01:00:51.582499328Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Elapsed":0.11} +{"Time":"2026-10-18T01:00:51.582505351Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-user-refuses-deletion (0.11s)\n"} +{"Time":"2026-10-18T01:00:51.582512745Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Elapsed":0.11} +{"Time":"2026-10-18T01:00:51.582518444Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-external-refuses-deletion (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582528165Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582533025Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582542448Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582547804Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582553431Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582560924Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/default/uncooperative-writers-excluded (0.72s)\n"} +{"Time":"2026-10-18T01:00:51.582607832Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Elapsed":0.72} +{"Time":"2026-10-18T01:00:51.582615785Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582626029Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582630997Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Elapsed":4.14} +{"Time":"2026-10-18T01:00:51.582643955Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":" --- PASS: TestSourceOperationPostgres/public (3.55s)\n"} +{"Time":"2026-10-18T01:00:51.582653811Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/create-reconcile-inverse (0.13s)\n"} +{"Time":"2026-10-18T01:00:51.582660718Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Elapsed":0.13} +{"Time":"2026-10-18T01:00:51.582668861Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/public/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582674515Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582679249Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/public/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582690376Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582695741Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:00:51.582704371Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:00:51.582708843Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582717491Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582722423Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582731242Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582745354Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582753803Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582759845Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back (0.09s)\n"} +{"Time":"2026-10-18T01:00:51.58276512Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Elapsed":0.09} +{"Time":"2026-10-18T01:00:51.582769989Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.58278586Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582791793Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/public/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T01:00:51.582801054Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T01:00:51.582805788Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582811315Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.58281586Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Elapsed":3.55} +{"Time":"2026-10-18T01:00:51.582820297Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":" --- PASS: TestSourceOperationPostgres/named (3.40s)\n"} +{"Time":"2026-10-18T01:00:51.582825761Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T01:00:51.582831216Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T01:00:51.582836978Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/named/exact-raw-preexisting-restoration (0.11s)\n"} +{"Time":"2026-10-18T01:00:51.582842407Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Elapsed":0.11} +{"Time":"2026-10-18T01:00:51.582851181Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/named/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582883976Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582894689Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:00:51.582900469Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:00:51.582905221Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582917232Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582922904Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-user-refuses-deletion (0.11s)\n"} +{"Time":"2026-10-18T01:00:51.582935853Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Elapsed":0.11} +{"Time":"2026-10-18T01:00:51.58295955Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582965736Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582970713Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.582975934Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.582992923Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.583000678Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.583005917Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/named/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T01:00:51.58301203Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T01:00:51.583027143Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T01:00:51.583035277Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T01:00:51.583040308Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Elapsed":3.4} +{"Time":"2026-10-18T01:00:51.583044583Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Elapsed":11.1} +{"Time":"2026-10-18T01:00:51.583048983Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"} +{"Time":"2026-10-18T01:00:51.610940014Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":11.409} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.json new file mode 100644 index 0000000..dcc2dc7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.json @@ -0,0 +1,200 @@ +{ + "action": "suite", + "returncode": 1, + "expected_returncode": 1, + "outcome_matches": true, + "seconds": 9.69, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-lock/db", + "/work/source/models/db", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-control-lock", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:137bcc05baf724abfcb9c48a2ef45d5064a6a163c4ba98269364eafb9a1096cd", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "omit-postgres-lock", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:137bcc05baf724abfcb9c48a2ef45d5064a6a163c4ba98269364eafb9a1096cd", + "production_source_changed": false + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:82043a82a461a942ff747c59238a3113b5d06de06a2d927c7946df3b22d89498" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.log new file mode 100644 index 0000000..fa78800 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt5.log @@ -0,0 +1,190 @@ +{"Time":"2026-10-18T01:05:38.768696462Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:05:38.92626254Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.92639155Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/attachments\n"} +{"Time":"2026-10-18T01:05:38.926402912Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.926418683Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/avatars\n"} +{"Time":"2026-10-18T01:05:38.926427466Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.926435888Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/repo-avatars\n"} +{"Time":"2026-10-18T01:05:38.926444121Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.92645202Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/repo-archive\n"} +{"Time":"2026-10-18T01:05:38.926459856Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.926467503Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/packages\n"} +{"Time":"2026-10-18T01:05:38.926475248Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.926482949Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2350079914/actions_log\n"} +{"Time":"2026-10-18T01:05:38.926490713Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:05:38.926498477Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:05:38 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:05:39.027752314Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T01:05:39.027797319Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T01:05:39.027806787Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default"} +{"Time":"2026-10-18T01:05:39.027810971Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":"=== RUN TestSourceOperationPostgres/default\n"} +{"Time":"2026-10-18T01:05:40.920709928Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse"} +{"Time":"2026-10-18T01:05:40.920794122Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/default/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:05:41.049140406Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:05:41.049189153Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/default/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:05:41.151528256Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:05:41.151568482Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/default/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:05:41.250733822Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply"} +{"Time":"2026-10-18T01:05:41.250772019Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:05:41.339755451Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse"} +{"Time":"2026-10-18T01:05:41.339807697Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:05:41.434209119Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:05:41.434247072Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:41.537815334Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:05:41.537857868Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:41.63902828Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:05:41.639110337Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:05:41.732981278Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:05:41.733017175Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:05:41.836224169Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:05:41.83626536Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/default/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:41.917406289Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" source_operation_postgres_test.go:282: \n"} +{"Time":"2026-10-18T01:05:41.917442547Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:282\n"} +{"Time":"2026-10-18T01:05:41.917447182Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:05:41.917450816Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" \tError: \tShould be true\n"} +{"Time":"2026-10-18T01:05:41.917454459Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" \tTest: \tTestSourceOperationPostgres/default/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:41.917457931Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" \tMessages: \tan uncooperative writer escaped the actual table lock\n"} +{"Time":"2026-10-18T01:05:41.917465126Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:05:41.917468246Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:05:42.022036233Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" source_operation_postgres_test.go:321: \n"} +{"Time":"2026-10-18T01:05:42.022084872Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:321\n"} +{"Time":"2026-10-18T01:05:42.02209067Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:05:42.022095612Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \tError: \tNot equal: \n"} +{"Time":"2026-10-18T01:05:42.022100102Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \t \texpected: 1\n"} +{"Time":"2026-10-18T01:05:42.022104521Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \t \tactual : 2\n"} +{"Time":"2026-10-18T01:05:42.022109652Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" \tTest: \tTestSourceOperationPostgres/default/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:05:42.022464426Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public"} +{"Time":"2026-10-18T01:05:42.02250623Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":"=== RUN TestSourceOperationPostgres/public\n"} +{"Time":"2026-10-18T01:05:44.045926419Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse"} +{"Time":"2026-10-18T01:05:44.045972876Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/public/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:05:44.169852241Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:05:44.16990684Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/public/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:05:44.271511536Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:05:44.271570797Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/public/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:05:44.37120008Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply"} +{"Time":"2026-10-18T01:05:44.37124458Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:05:44.474344957Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse"} +{"Time":"2026-10-18T01:05:44.474396031Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:05:44.585397268Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:05:44.585453536Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:44.711724758Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:05:44.711821964Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:44.832624834Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:05:44.832682184Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:05:44.949311021Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:05:44.949368633Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:05:45.069008803Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:05:45.069125558Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:45.16846189Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" source_operation_postgres_test.go:282: \n"} +{"Time":"2026-10-18T01:05:45.168519799Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:282\n"} +{"Time":"2026-10-18T01:05:45.168531494Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:05:45.168538293Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" \tError: \tShould be true\n"} +{"Time":"2026-10-18T01:05:45.168554867Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" \tTest: \tTestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:45.168562685Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" \tMessages: \tan uncooperative writer escaped the actual table lock\n"} +{"Time":"2026-10-18T01:05:45.1685708Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:05:45.168575543Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:05:45.280524395Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named"} +{"Time":"2026-10-18T01:05:45.2805772Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":"=== RUN TestSourceOperationPostgres/named\n"} +{"Time":"2026-10-18T01:05:47.282273653Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse"} +{"Time":"2026-10-18T01:05:47.282315772Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/named/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:05:47.395342924Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:05:47.395458814Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/named/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:05:47.490998158Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:05:47.491045517Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/named/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:05:47.588881435Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply"} +{"Time":"2026-10-18T01:05:47.588938217Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:05:47.682010877Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse"} +{"Time":"2026-10-18T01:05:47.682066117Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:05:47.780868465Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:05:47.780921081Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:47.881999712Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:05:47.882045935Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:05:47.97937744Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:05:47.97942148Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:05:48.07641057Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:05:48.076448199Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:05:48.178631251Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:05:48.1787176Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:48.2645986Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" source_operation_postgres_test.go:282: \n"} +{"Time":"2026-10-18T01:05:48.264650927Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:282\n"} +{"Time":"2026-10-18T01:05:48.264663245Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:05:48.264670044Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" \tError: \tShould be true\n"} +{"Time":"2026-10-18T01:05:48.26467466Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" \tTest: \tTestSourceOperationPostgres/named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:05:48.264698051Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" \tMessages: \tan uncooperative writer escaped the actual table lock\n"} +{"Time":"2026-10-18T01:05:48.264711018Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:05:48.264714499Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:05:48.368321174Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- FAIL: TestSourceOperationPostgres (9.34s)\n"} +{"Time":"2026-10-18T01:05:48.368377261Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":" --- FAIL: TestSourceOperationPostgres/default (2.99s)\n"} +{"Time":"2026-10-18T01:05:48.368390012Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/create-reconcile-inverse (0.13s)\n"} +{"Time":"2026-10-18T01:05:48.368399372Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Elapsed":0.13} +{"Time":"2026-10-18T01:05:48.368493322Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/default/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368507696Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368516019Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/default/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368524549Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368531384Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:05:48.368539752Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:05:48.368546786Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-inverse (0.09s)\n"} +{"Time":"2026-10-18T01:05:48.368554982Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Elapsed":0.09} +{"Time":"2026-10-18T01:05:48.368561971Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368569762Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368576344Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368600429Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368609113Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back (0.09s)\n"} +{"Time":"2026-10-18T01:05:48.368617201Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Elapsed":0.09} +{"Time":"2026-10-18T01:05:48.368624199Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368631674Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.36863869Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" --- FAIL: TestSourceOperationPostgres/default/uncooperative-writers-excluded (0.08s)\n"} +{"Time":"2026-10-18T01:05:48.3686541Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Elapsed":0.08} +{"Time":"2026-10-18T01:05:48.368661467Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" --- FAIL: TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.3686697Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368675597Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Elapsed":2.99} +{"Time":"2026-10-18T01:05:48.368682775Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":" --- FAIL: TestSourceOperationPostgres/public (3.26s)\n"} +{"Time":"2026-10-18T01:05:48.368690764Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T01:05:48.368698519Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T01:05:48.368705065Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/public/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368712766Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368719782Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/public/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368727627Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368734462Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-apply (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.36875081Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368759124Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-inverse (0.11s)\n"} +{"Time":"2026-10-18T01:05:48.368767095Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Elapsed":0.11} +{"Time":"2026-10-18T01:05:48.368778845Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-user-refuses-deletion (0.13s)\n"} +{"Time":"2026-10-18T01:05:48.368786572Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Elapsed":0.13} +{"Time":"2026-10-18T01:05:48.368793651Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-external-refuses-deletion (0.12s)\n"} +{"Time":"2026-10-18T01:05:48.368801541Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Elapsed":0.12} +{"Time":"2026-10-18T01:05:48.368807943Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back (0.12s)\n"} +{"Time":"2026-10-18T01:05:48.368868278Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Elapsed":0.12} +{"Time":"2026-10-18T01:05:48.368877412Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics (0.12s)\n"} +{"Time":"2026-10-18T01:05:48.368885924Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Elapsed":0.12} +{"Time":"2026-10-18T01:05:48.368892814Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" --- FAIL: TestSourceOperationPostgres/public/uncooperative-writers-excluded (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368900451Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368908026Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner (0.11s)\n"} +{"Time":"2026-10-18T01:05:48.368916691Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Elapsed":0.11} +{"Time":"2026-10-18T01:05:48.368923003Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Elapsed":3.26} +{"Time":"2026-10-18T01:05:48.368929586Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":" --- FAIL: TestSourceOperationPostgres/named (3.09s)\n"} +{"Time":"2026-10-18T01:05:48.368938053Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/create-reconcile-inverse (0.11s)\n"} +{"Time":"2026-10-18T01:05:48.368957332Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Elapsed":0.11} +{"Time":"2026-10-18T01:05:48.368965953Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/named/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368973779Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368980597Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/named/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.368988414Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.368995195Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:05:48.369002662Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:05:48.369009145Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369026458Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369034159Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369042275Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369049335Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369057063Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369063817Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369070958Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369077604Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" --- PASS: TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369086053Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369092537Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" --- FAIL: TestSourceOperationPostgres/named/uncooperative-writers-excluded (0.09s)\n"} +{"Time":"2026-10-18T01:05:48.369109958Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Elapsed":0.09} +{"Time":"2026-10-18T01:05:48.36911783Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T01:05:48.369124638Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T01:05:48.369130905Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Elapsed":3.09} +{"Time":"2026-10-18T01:05:48.3691371Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Elapsed":9.34} +{"Time":"2026-10-18T01:05:48.369144115Z","Action":"output","Package":"forgejo.org/models/auth","Output":"FAIL\n"} +{"Time":"2026-10-18T01:05:48.397105421Z","Action":"fail","Package":"forgejo.org/models/auth","Elapsed":9.628} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.json new file mode 100644 index 0000000..e327a51 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.json @@ -0,0 +1,200 @@ +{ + "action": "suite", + "returncode": 1, + "expected_returncode": 1, + "outcome_matches": true, + "seconds": 11.35, + "argv": [ + "bwrap", + "--unshare-pid", + "--die-with-parent", + "--new-session", + "--unshare-net", + "--ro-bind", + "/usr", + "/usr", + "--ro-bind", + "/lib", + "/lib", + "--ro-bind", + "/lib64", + "/lib64", + "--symlink", + "usr/bin", + "/bin", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--bind", + "/tmp/custodian-forgejo-successor-r3", + "/work", + "--ro-bind", + "/home/worsch/go/pkg/mod/cache/download", + "/cached", + "--ro-bind", + "/etc/ssl/certs", + "/etc/ssl/certs", + "--ro-bind", + "/etc/resolv.conf", + "/etc/resolv.conf", + "--chdir", + "/work/source", + "--clearenv", + "--ro-bind", + "/tmp/custodian-forgejo-successor-attempt2/modules", + "/work/modules", + "--bind", + "/tmp/custodian-forgejo-successor-attempt2/gocache", + "/work/gocache", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/forgejo-oidc", + "--bind", + "/tmp/custodian-forgejo-successor-r3/descriptor-fixtures", + "/run/writable-input", + "--setenv", + "PATH", + "/work/go/bin:/usr/bin:/bin", + "--setenv", + "HOME", + "/work/home", + "--setenv", + "GOPATH", + "/work/gopath", + "--setenv", + "GOTOOLCHAIN", + "local", + "--setenv", + "GOPROXY", + "off", + "--setenv", + "GOSUMDB", + "off", + "--setenv", + "GOVCS", + "*:off", + "--setenv", + "GOMODCACHE", + "/work/modules", + "--setenv", + "GOCACHE", + "/work/gocache", + "--setenv", + "GOMAXPROCS", + "4", + "--setenv", + "CGO_ENABLED", + "1", + "--setenv", + "GIT_CONFIG_NOSYSTEM", + "1", + "--setenv", + "GIT_CONFIG_GLOBAL", + "/work/home/test.gitconfig", + "--setenv", + "GIT_TERMINAL_PROMPT", + "0", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private/auth", + "/work/source/models/auth", + "--bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private", + "/pg-work", + "--dir", + "/run", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-r1-attempt3/socket", + "/run/source-operation-postgres", + "--setenv", + "CUSTODIAN_PG_SOCKET", + "/run/source-operation-postgres", + "--ro-bind", + "/tmp/custodian-forgejo-postgres-tests-r1-control-private/db", + "/work/source/models/db", + "--", + "go", + "tool", + "test2json", + "-t", + "-p", + "forgejo.org/models/auth", + "/pg-work/models-auth.test", + "-test.v", + "-test.run=^TestSourceOperationPostgres$", + "-test.count=1" + ], + "workspace": "/tmp/custodian-forgejo-postgres-tests-r1-control-private", + "source_pins": { + "access_token.go": "sha256:c3131e03c24356fb09fdd600efd17d8054063534355a4c5a3b769f840a58c8ac", + "access_token_scope.go": "sha256:36173156237cc3c3231da78fc9c7e503391ea29c29496721bf4f6b197ab128d3", + "access_token_scope_test.go": "sha256:738bff8f60339a3b4cbd21c31f52812eacde1435fcf55ea657c2a950c262de7d", + "access_token_test.go": "sha256:0121b13b075e6dadb5f6bf5899ceb815786d386cd75214566545b1a3cbe5e721", + "auth_token.go": "sha256:e652217daeffab3c9918efaab56ba4a0a0e8024071840cd8bb85925fe2568bd9", + "main_test.go": "sha256:1c78be2861df2215936939b085bc2bac431f607731fe68da85cfc9c95a4eac87", + "oauth2.go": "sha256:626c075138a8e1dd22d4814b11c6d6acd04e8aae2443e47017de9a289b8f55ac", + "oauth2_list.go": "sha256:2665b0833370d4cb9d0a30e43bd86625f4f707e26a8e5bef53559816aafb7e91", + "oauth2_test.go": "sha256:66e671311715001e2c8119daa944667b9744b4a701732007c6f4510d1daf40ca", + "session.go": "sha256:25d934eb96d2b540a51b7337650b58123ad4ee7e1c61547e7efc1c36eb12c26d", + "session_test.go": "sha256:cae8e667a88143cf7ce3238e8adc9e87fd7f0bdf1b2c0d9b1736c2de38485659", + "source.go": "sha256:33ec2f66652bcb442cd80c36db0a2ad7305e1ee9c9c610d054720eea8ab18e4c", + "source_operation.go": "sha256:0b9e248b685a950cfbf31f76aafd54e1b99272402a2f05bc6d8b742bc2657d82", + "source_operation_internal_test.go": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb", + "source_operation_postgres_export_test.go": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d", + "source_operation_postgres_test.go": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe", + "source_operation_process_test.go": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd", + "source_operation_test.go": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428", + "source_test.go": "sha256:1ad3f9b198155193fd8193f50084889cc949192abf64c060a5ce3043f2ccb212", + "two_factor.go": "sha256:d3a6e0b348b225d0fc012fdd5a6275736a1b324ec4cd1c013ae80a12531e0b8e", + "two_factor_test.go": "sha256:6a88cb12d204a3b3de49d584d589b1c5a97bfb795160ae93b87707f64bece4fe", + "twofactor.go": "sha256:905e1679be552346758b34c8ee7c950628d965d8bd3946b9b63c6797f41625d6", + "webauthn.go": "sha256:0f9adac61a225854d579ed71338303a30a5abf26e4c115a0afefaa4fd199200e", + "webauthn_test.go": "sha256:03f3487f7c05566e71399ff548a95400a66ed7afc96bbf31365aa7abb79dfa69" + }, + "database_source_pins": { + "collation.go": "sha256:9922312734c5cc2a5474b322b88214586916b320969172708f48c7b6eca40e7d", + "common.go": "sha256:f0c626ebedaedb8681a4f3a3531441c49f3e0d305b20730aa47b267b6e16b3fd", + "consistency.go": "sha256:c771f62de8511bfc307583a4585190aba8b38ca2ac76eab200bc901aa15b7bad", + "context.go": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7", + "context_committer_test.go": "sha256:83cf7fbfc32aff358221abadee63e96a803a4136c9851cf696c543dc5176d777", + "context_test.go": "sha256:677f6be23f1c855a154de6078a540b60e17c737becfae6115e55ff6d81c3a937", + "convert.go": "sha256:48e62893b598b767dccbf414c0acffbc26c6cfb6cb22ba79eb5b66dbb6c8193a", + "engine.go": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "engine_test.go": "sha256:ec78585da5d8b33deb0b55ba3ffd9d8ff4df704ffd62c2d95c2a6ffddd5bc309", + "error.go": "sha256:ca0ead3529d977f90930974200a0a751366629590dae516cb54245d9c75bb280", + "index.go": "sha256:fdfdaf6da41f317d51665bfc03684074aadf5fb32607d80c15ee9b33fdc89f09", + "index_test.go": "sha256:d46ea9d96fc9b51e477ebecb5cfa0e15fc8e13a00c5c84ce620dd23e73663558", + "iterate.go": "sha256:0512e2f2a3785daec26f434f6b55c37e8cd590c1b49d2068f42160c424757e28", + "iterate_test.go": "sha256:8b9eb9d391bd99bddf6b3da5744bd80088eb4d8446c7f51dd3299abe1b8a5ebe", + "list.go": "sha256:e23e7056f9dbce52d85ca922fa4eadb834a6ee3a5dbcafe82485038332b798e0", + "list_test.go": "sha256:5bc42a869931470daf503b51bd3dbf5cb092d37d8cc4e03f80a21f564beadf6a", + "log.go": "sha256:6f080111c46868e337d4d6dd16c56a5743c13b86b412df219701030cd1718a01", + "main_test.go": "sha256:d9eb5ba4ab294ebb2c865969479d6d73db21669f24cc5454ac42f6dbd8825ef3", + "name.go": "sha256:e486e89a36103f56af54f5f39a9b166ac0b08800abaccc2857ab324777dda6b5", + "search.go": "sha256:b488e9a93f95d28450929896ce648e67f6772e1cd8662f7a0a1a8988a24dbfa2", + "sensitive_sql.go": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584", + "sensitive_sql_logging_test.go": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7", + "sensitive_sql_test.go": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b", + "sequence.go": "sha256:c0e4c7fbaa93961d9711143cfcf4bbb32f197985d21b77424946fbe3a39b38eb", + "sql_postgres_with_schema.go": "sha256:e5941e859ada7831b400fd3143e58f91b1058cb32779d6a4466af01617cf6e7a", + "tracing_context_test.go": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + "original_composite_patch": "sha256:15cb7f13b1b1d1095cc344176ca337f705333a436a70ae803fcfd3d7f802ccf4", + "runtime": "custodian-forgejo-pg-96ce905f445a", + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "controlled_mutation": { + "mutation": "omit-private-context", + "original_source_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31", + "controlled_source_sha256": "sha256:0b9e248b685a950cfbf31f76aafd54e1b99272402a2f05bc6d8b742bc2657d82", + "production_source_changed": false + }, + "network": "disabled; only the exact disposable database Unix socket is bound", + "production_database": false, + "actual_authenticated_operation": false, + "real_credentials": false, + "test_binary_sha256": "sha256:e5acaf24f87a94f58b1cd9000f04a08186b35f29e6e848835ecd3c7105a9de72" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.log new file mode 100644 index 0000000..b15da21 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/postgres-suite-attempt6.log @@ -0,0 +1,186 @@ +{"Time":"2026-10-18T01:06:37.272075993Z","Action":"start","Package":"forgejo.org/models/auth"} +{"Time":"2026-10-18T01:06:37.413624041Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413704087Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/attachments\n"} +{"Time":"2026-10-18T01:06:37.413708848Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413715079Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/avatars\n"} +{"Time":"2026-10-18T01:06:37.413718587Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413721941Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/repo-avatars\n"} +{"Time":"2026-10-18T01:06:37.413725277Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413728406Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/repo-archive\n"} +{"Time":"2026-10-18T01:06:37.413731553Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413734655Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/packages\n"} +{"Time":"2026-10-18T01:06:37.41373773Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413740805Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata302694419/actions_log\n"} +{"Time":"2026-10-18T01:06:37.413743889Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"} +{"Time":"2026-10-18T01:06:37.413746937Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 01:06:37 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /pg-work/data/actions_artifacts\n"} +{"Time":"2026-10-18T01:06:37.522368095Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres"} +{"Time":"2026-10-18T01:06:37.522422613Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"=== RUN TestSourceOperationPostgres\n"} +{"Time":"2026-10-18T01:06:37.522431603Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default"} +{"Time":"2026-10-18T01:06:37.522436923Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":"=== RUN TestSourceOperationPostgres/default\n"} +{"Time":"2026-10-18T01:06:39.38351702Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse"} +{"Time":"2026-10-18T01:06:39.383598221Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/default/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:06:39.496192274Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:06:39.496228243Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/default/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:06:39.596076806Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:06:39.596116148Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/default/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:06:39.697806777Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply"} +{"Time":"2026-10-18T01:06:39.697844866Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:06:39.790766219Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse"} +{"Time":"2026-10-18T01:06:39.790805589Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/default/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:06:39.887011811Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:06:39.887047754Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:39.982500658Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:06:39.982539604Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/default/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:40.078461333Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:06:40.078501586Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:06:40.173324905Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:06:40.173363417Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:40.269530884Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" source_operation_postgres_test.go:247: \n"} +{"Time":"2026-10-18T01:06:40.269584158Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:247\n"} +{"Time":"2026-10-18T01:06:40.269590434Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:06:40.26959845Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" \tError: \t\"[Slow SQL Query] BEGIN TRANSACTION [] - 335.752µs\\n[Slow SQL Query] LOCK TABLE login_source_operation, login_source, \\\"user\\\", external_login_user IN EXCLUSIVE MODE [] - 163.167µs\\n[Slow SQL Query] SELECT \\\"operation_id\\\", \\\"case_id\\\", \\\"target_name\\\", \\\"target_id\\\", \\\"phase\\\", \\\"before_state\\\", \\\"desired_state\\\", \\\"post_state\\\" FROM \\\"login_source_operation\\\" WHERE \\\"operation_id\\\"=$1 LIMIT 1 [postgres-error-operation] - 657.871µs\\n[Slow SQL Query] SELECT \\\"id\\\", \\\"type\\\", \\\"name\\\", \\\"is_active\\\", \\\"is_sync_enabled\\\", \\\"cfg\\\", \\\"created_unix\\\", \\\"updated_unix\\\" FROM \\\"login_source\\\" ORDER BY id [] - 425.834µs\\n[Slow SQL Query] INSERT INTO \\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\"} +{"Time":"2026-10-18T01:06:40.269628144Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285600 1792285600] - 862.132µs\\n[Error SQL Query] INSERT INTO \\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"Admi"} +{"Time":"2026-10-18T01:06:40.269647135Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":"nGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285600 1792285600] - pq: {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"}\\n[Slow SQL Query] ROLLBACK [] - 288.315µs\" does not contain \"[Error SQL Query] [owner-confined query]\"\n"} +{"Time":"2026-10-18T01:06:40.269663339Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" \tTest: \tTestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:40.269670029Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:06:40.269674385Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/default/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:06:40.962767266Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:06:40.962806248Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:06:41.060420719Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public"} +{"Time":"2026-10-18T01:06:41.060475904Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":"=== RUN TestSourceOperationPostgres/public\n"} +{"Time":"2026-10-18T01:06:42.879901435Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse"} +{"Time":"2026-10-18T01:06:42.879936711Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/public/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:06:43.017177562Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:06:43.017220791Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/public/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:06:43.113976457Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:06:43.114021201Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/public/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:06:43.210664143Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply"} +{"Time":"2026-10-18T01:06:43.210705694Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:06:43.308260517Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse"} +{"Time":"2026-10-18T01:06:43.308303457Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/public/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:06:43.424171027Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:06:43.424242344Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:43.53111742Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:06:43.531165906Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/public/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:43.628005322Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:06:43.628048172Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:06:43.725621901Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:06:43.725657204Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:43.830330636Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" source_operation_postgres_test.go:247: \n"} +{"Time":"2026-10-18T01:06:43.830395028Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:247\n"} +{"Time":"2026-10-18T01:06:43.830424434Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:06:43.830433361Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" \tError: \t\"[Slow SQL Query] BEGIN TRANSACTION [] - 334.508µs\\n[Slow SQL Query] LOCK TABLE login_source_operation, login_source, \\\"user\\\", external_login_user IN EXCLUSIVE MODE [] - 294.088µs\\n[Slow SQL Query] SELECT \\\"operation_id\\\", \\\"case_id\\\", \\\"target_name\\\", \\\"target_id\\\", \\\"phase\\\", \\\"before_state\\\", \\\"desired_state\\\", \\\"post_state\\\" FROM \\\"public\\\".\\\"login_source_operation\\\" WHERE \\\"operation_id\\\"=$1 LIMIT 1 [postgres-error-operation] - 816.656µs\\n[Slow SQL Query] SELECT \\\"id\\\", \\\"type\\\", \\\"name\\\", \\\"is_active\\\", \\\"is_sync_enabled\\\", \\\"cfg\\\", \\\"created_unix\\\", \\\"updated_unix\\\" FROM \\\"public\\\".\\\"login_source\\\" ORDER BY id [] - 796.93µs\\n[Slow SQL Query] INSERT INTO \\\"public\\\".\\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenID"} +{"Time":"2026-10-18T01:06:43.830452522Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":"ConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285603 1792285603] - 1.757047ms\\n[Error SQL Query] INSERT INTO \\\"public\\\".\\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaim"} +{"Time":"2026-10-18T01:06:43.830514327Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":"Value\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285603 1792285603] - pq: {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"}\\n[Slow SQL Query] ROLLBACK [] - 341.213µs\" does not contain \"[Error SQL Query] [owner-confined query]\"\n"} +{"Time":"2026-10-18T01:06:43.830529953Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" \tTest: \tTestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:43.830538619Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:06:43.83054412Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/public/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:06:44.53295111Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:06:44.532986602Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:06:44.627549722Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named"} +{"Time":"2026-10-18T01:06:44.627631175Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":"=== RUN TestSourceOperationPostgres/named\n"} +{"Time":"2026-10-18T01:06:50.025184426Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse"} +{"Time":"2026-10-18T01:06:50.025261487Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":"=== RUN TestSourceOperationPostgres/named/create-reconcile-inverse\n"} +{"Time":"2026-10-18T01:06:50.149811008Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration"} +{"Time":"2026-10-18T01:06:50.14985439Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":"=== RUN TestSourceOperationPostgres/named/exact-raw-preexisting-restoration\n"} +{"Time":"2026-10-18T01:06:50.24929962Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives"} +{"Time":"2026-10-18T01:06:50.249336834Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":"=== RUN TestSourceOperationPostgres/named/unchanged-preexisting-survives\n"} +{"Time":"2026-10-18T01:06:50.352989533Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply"} +{"Time":"2026-10-18T01:06:50.353027991Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-apply\n"} +{"Time":"2026-10-18T01:06:50.446647311Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse"} +{"Time":"2026-10-18T01:06:50.446690332Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":"=== RUN TestSourceOperationPostgres/named/private-drift-before-inverse\n"} +{"Time":"2026-10-18T01:06:50.549036411Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion"} +{"Time":"2026-10-18T01:06:50.549077349Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-user-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:50.645560295Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion"} +{"Time":"2026-10-18T01:06:50.645618583Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":"=== RUN TestSourceOperationPostgres/named/linked-external-refuses-deletion\n"} +{"Time":"2026-10-18T01:06:50.737879962Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back"} +{"Time":"2026-10-18T01:06:50.737942515Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":"=== RUN TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back\n"} +{"Time":"2026-10-18T01:06:50.829933963Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics"} +{"Time":"2026-10-18T01:06:50.829970293Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":"=== RUN TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:50.922610464Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" source_operation_postgres_test.go:247: \n"} +{"Time":"2026-10-18T01:06:50.922647002Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" \tError Trace:\t/work/source/models/auth/source_operation_postgres_test.go:247\n"} +{"Time":"2026-10-18T01:06:50.922663016Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" \t \t\t\t\t/work/source/models/auth/source_operation_postgres_test.go:106\n"} +{"Time":"2026-10-18T01:06:50.9226706Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" \tError: \t\"[Slow SQL Query] BEGIN TRANSACTION [] - 414.267µs\\n[Slow SQL Query] LOCK TABLE login_source_operation, login_source, \\\"user\\\", external_login_user IN EXCLUSIVE MODE [] - 368.37µs\\n[Slow SQL Query] SELECT \\\"operation_id\\\", \\\"case_id\\\", \\\"target_name\\\", \\\"target_id\\\", \\\"phase\\\", \\\"before_state\\\", \\\"desired_state\\\", \\\"post_state\\\" FROM \\\"custodian_named\\\".\\\"login_source_operation\\\" WHERE \\\"operation_id\\\"=$1 LIMIT 1 [postgres-error-operation] - 679.184µs\\n[Slow SQL Query] SELECT \\\"id\\\", \\\"type\\\", \\\"name\\\", \\\"is_active\\\", \\\"is_sync_enabled\\\", \\\"cfg\\\", \\\"created_unix\\\", \\\"updated_unix\\\" FROM \\\"custodian_named\\\".\\\"login_source\\\" ORDER BY id [] - 638.32µs\\n[Slow SQL Query] INSERT INTO \\\"custodian_named\\\".\\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-p"} +{"Time":"2026-10-18T01:06:50.922680023Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":"rivate-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285610 1792285610] - 946.901µs\\n[Error SQL Query] INSERT INTO \\\"custodian_named\\\".\\\"login_source\\\" (\\\"type\\\",\\\"name\\\",\\\"is_active\\\",\\\"is_sync_enabled\\\",\\\"cfg\\\",\\\"created_unix\\\",\\\"updated_unix\\\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING \\\"id\\\" [OAuth2 operation-test false false {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\""} +{"Time":"2026-10-18T01:06:50.922688445Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"} 1792285610 1792285610] - pq: {\\\"Provider\\\":\\\"openidConnect\\\",\\\"ClientID\\\":\\\"synthetic-client\\\",\\\"ClientSecret\\\":\\\"synthetic-private-original\\\",\\\"OpenIDConnectAutoDiscoveryURL\\\":\\\"\\\",\\\"CustomURLMapping\\\":null,\\\"IconURL\\\":\\\"\\\",\\\"Scopes\\\":[\\\"openid\\\",\\\"profile\\\",\\\"groups\\\"],\\\"AttributeSSHPublicKey\\\":\\\"\\\",\\\"RequiredClaimName\\\":\\\"groups\\\",\\\"RequiredClaimValues\\\":[\\\"g1\\\",\\\"g2\\\",\\\"g3\\\"],\\\"RequiredClaimValue\\\":\\\"\\\",\\\"GroupClaimName\\\":\\\"\\\",\\\"AdminGroup\\\":\\\"\\\",\\\"GroupTeamMap\\\":\\\"\\\",\\\"GroupTeamMapRemoval\\\":false,\\\"RestrictedGroup\\\":\\\"\\\"}\\n[Slow SQL Query] ROLLBACK [] - 320.461µs\" does not contain \"[Error SQL Query] [owner-confined query]\"\n"} +{"Time":"2026-10-18T01:06:50.922709067Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" \tTest: \tTestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics\n"} +{"Time":"2026-10-18T01:06:50.922714892Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded"} +{"Time":"2026-10-18T01:06:50.922718382Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":"=== RUN TestSourceOperationPostgres/named/uncooperative-writers-excluded\n"} +{"Time":"2026-10-18T01:06:51.624657036Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner"} +{"Time":"2026-10-18T01:06:51.624706866Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":"=== RUN TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner\n"} +{"Time":"2026-10-18T01:06:52.113053564Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Output":"--- FAIL: TestSourceOperationPostgres (11.01s)\n"} +{"Time":"2026-10-18T01:06:52.11313976Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Output":" --- FAIL: TestSourceOperationPostgres/default (3.54s)\n"} +{"Time":"2026-10-18T01:06:52.113151392Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/create-reconcile-inverse (0.11s)\n"} +{"Time":"2026-10-18T01:06:52.11316426Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/create-reconcile-inverse","Elapsed":0.11} +{"Time":"2026-10-18T01:06:52.113176199Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/default/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.11318637Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113192231Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/default/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113200834Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113210076Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.113219743Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113228859Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/default/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.11327959Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113285208Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113290222Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113298049Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/default/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113305849Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.11331235Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.11332033Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/unexpected-trigger-post-state-rolls-back","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113327355Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Output":" --- FAIL: TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113333604Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113340619Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/default/uncooperative-writers-excluded (0.69s)\n"} +{"Time":"2026-10-18T01:06:52.113348446Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/uncooperative-writers-excluded","Elapsed":0.69} +{"Time":"2026-10-18T01:06:52.113355687Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113363Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default/concurrent-preparation-has-one-owner","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113367635Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/default","Elapsed":3.54} +{"Time":"2026-10-18T01:06:52.113374912Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Output":" --- FAIL: TestSourceOperationPostgres/public (3.57s)\n"} +{"Time":"2026-10-18T01:06:52.113383Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/create-reconcile-inverse (0.14s)\n"} +{"Time":"2026-10-18T01:06:52.113390962Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/create-reconcile-inverse","Elapsed":0.14} +{"Time":"2026-10-18T01:06:52.113397563Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/public/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.11341374Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.11341934Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/public/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113427257Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113434453Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-apply (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113442253Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-apply","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113448132Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/public/private-drift-before-inverse (0.12s)\n"} +{"Time":"2026-10-18T01:06:52.113453993Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/private-drift-before-inverse","Elapsed":0.12} +{"Time":"2026-10-18T01:06:52.113467798Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-user-refuses-deletion (0.11s)\n"} +{"Time":"2026-10-18T01:06:52.113475039Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-user-refuses-deletion","Elapsed":0.11} +{"Time":"2026-10-18T01:06:52.113480459Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/public/linked-external-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113488331Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/linked-external-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.11349256Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113517592Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/unexpected-trigger-post-state-rolls-back","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113522849Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Output":" --- FAIL: TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113530667Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/driver-error-rollback-and-private-diagnostics","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113544129Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/public/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T01:06:52.113550604Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T01:06:52.113554887Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.11356789Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public/concurrent-preparation-has-one-owner","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113573057Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/public","Elapsed":3.57} +{"Time":"2026-10-18T01:06:52.113577169Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Output":" --- FAIL: TestSourceOperationPostgres/named (3.90s)\n"} +{"Time":"2026-10-18T01:06:52.113582227Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/create-reconcile-inverse (0.12s)\n"} +{"Time":"2026-10-18T01:06:52.11358697Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/create-reconcile-inverse","Elapsed":0.12} +{"Time":"2026-10-18T01:06:52.113593661Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Output":" --- PASS: TestSourceOperationPostgres/named/exact-raw-preexisting-restoration (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113603869Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/exact-raw-preexisting-restoration","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113608071Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Output":" --- PASS: TestSourceOperationPostgres/named/unchanged-preexisting-survives (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113615826Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unchanged-preexisting-survives","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113620181Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-apply (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.113624852Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-apply","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113629216Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Output":" --- PASS: TestSourceOperationPostgres/named/private-drift-before-inverse (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.113633914Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/private-drift-before-inverse","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113637999Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-user-refuses-deletion (0.10s)\n"} +{"Time":"2026-10-18T01:06:52.11364505Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-user-refuses-deletion","Elapsed":0.1} +{"Time":"2026-10-18T01:06:52.113649785Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Output":" --- PASS: TestSourceOperationPostgres/named/linked-external-refuses-deletion (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.113654483Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/linked-external-refuses-deletion","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113660488Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Output":" --- PASS: TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.113672183Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/unexpected-trigger-post-state-rolls-back","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113676773Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Output":" --- FAIL: TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics (0.09s)\n"} +{"Time":"2026-10-18T01:06:52.113681597Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/driver-error-rollback-and-private-diagnostics","Elapsed":0.09} +{"Time":"2026-10-18T01:06:52.113689163Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Output":" --- PASS: TestSourceOperationPostgres/named/uncooperative-writers-excluded (0.70s)\n"} +{"Time":"2026-10-18T01:06:52.11369387Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/uncooperative-writers-excluded","Elapsed":0.7} +{"Time":"2026-10-18T01:06:52.113698Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Output":" --- PASS: TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner (0.49s)\n"} +{"Time":"2026-10-18T01:06:52.113706756Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named/concurrent-preparation-has-one-owner","Elapsed":0.49} +{"Time":"2026-10-18T01:06:52.113710831Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres/named","Elapsed":3.9} +{"Time":"2026-10-18T01:06:52.11371828Z","Action":"fail","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPostgres","Elapsed":11.01} +{"Time":"2026-10-18T01:06:52.113722337Z","Action":"output","Package":"forgejo.org/models/auth","Output":"FAIL\n"} +{"Time":"2026-10-18T01:06:52.139381233Z","Action":"fail","Package":"forgejo.org/models/auth","Elapsed":11.287} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test.go new file mode 100644 index 0000000..5389475 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test.go @@ -0,0 +1,309 @@ +//go:build source_operation_postgres + +package auth_test + +import ( + "context" + "errors" + "os" + "sync" + "testing" + "time" + + auth "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/modules/setting" + oauth2 "forgejo.org/services/auth/source/oauth2" + + "github.com/lib/pq" + "github.com/stretchr/testify/require" + "xorm.io/xorm" +) + +// The real Forgejo engine/driver, registered model schemas and public operation +// functions run. Only the database, application configuration and actors are +// synthetic. No deployed owner database or authenticated operation is claimed. +func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) { + t.Helper() + require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required") + oldSettings, oldContext := setting.Database, db.DefaultContext + oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine) + require.True(t, ok) + raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+schema) + require.NoError(t, err) + _, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + schema + `"`) + require.NoError(t, err) + setting.Database.Type = "postgres" + setting.Database.Host = "/run/source-operation-postgres" + setting.Database.Name = "custodian_source" + setting.Database.User = "custodian_test" + setting.Database.Passwd = "" + setting.Database.Schema = schema + setting.Database.SSLMode = "disable" + setting.Database.LogSQL = false + setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2 + setting.Database.SlowQueryThreshold = 0 + require.NoError(t, db.InitEngine(context.Background())) + engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) + require.True(t, ok) + t.Cleanup(func() { + _ = engine.Close() + _ = raw.Close() + setting.Database = oldSettings + db.SetDefaultEngine(oldContext, oldEngine) + }) + version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema") + require.NoError(t, err) + require.Equal(t, "16", string(version[0]["major"])) + require.Equal(t, schema, string(version[0]["schema"])) + require.NoError(t, db.SyncAllTables()) + auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) + return db.DefaultContext, engine, raw +} + +func resetPostgresOperations(t *testing.T, engine *xorm.Engine) { + t.Helper() + _, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`) + require.NoError(t, err) + _, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`) + require.NoError(t, err) +} + +func TestSourceOperationPostgres(t *testing.T) { + for _, schema := range []string{"public", "custodian_named"} { + t.Run(schema, func(t *testing.T) { + ctx, engine, raw := postgresOperationDB(t, schema) + run := func(name string, f func(*testing.T)) { + t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) }) + } + run("create-reconcile-inverse", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource()) + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + require.Positive(t, r.SourceID) + post := operationRows(t, ctx) + again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, r, again) + require.Equal(t, post, operationRows(t, ctx)) + _, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + require.Equal(t, before, operationRows(t, ctx)) + _, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + }) + run("exact-raw-preexisting-restoration", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" + _, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + _, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + run("unchanged-preexisting-survives", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, "unchanged", r.Phase) + _, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + for _, stage := range []string{"before-apply", "before-inverse"} { + run("private-drift-"+stage, func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s) + require.NoError(t, err) + if stage == "before-inverse" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + require.NoError(t, err) + } + _, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + if stage == "before-apply" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } else { + _, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + }) + } + for _, kind := range []string{"user", "external"} { + run("linked-"+kind+"-refuses-deletion", func(t *testing.T) { + _, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource()) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.NoError(t, err) + if kind == "user" { + _, err = engine.Exec(`INSERT INTO "user" (id, lower_name, name, login_source) VALUES (1, 'synthetic', 'synthetic', ?)`, r.SourceID) + } else { + _, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID) + } + require.NoError(t, err) + before := operationRows(t, ctx) + _, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.True(t, auth.IsErrSourceInUse(err)) + require.Equal(t, before, operationRows(t, ctx)) + var count int64 + if kind == "user" { + count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count() + } else { + count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count() + } + require.NoError(t, err) + require.EqualValues(t, 1, count) + }) + } + run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource()) + require.NoError(t, err) + _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`) + require.NoError(t, err) + _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + }) + run("uncooperative-writers-excluded", func(t *testing.T) { + locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1) + var once sync.Once + releaseLock := func() { once.Do(func() { close(release) }) } + defer releaseLock() + go func() { + done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil }) + }() + select { + case <-locked: + case err := <-done: + t.Fatalf("lock acquisition refused: %v", err) + case <-time.After(5 * time.Second): + t.Fatal("lock acquisition timed out") + } + statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`} + for _, statement := range statements { + session := raw.NewSession() + require.NoError(t, session.Begin()) + _, err := session.Exec(`SET LOCAL lock_timeout='150ms'`) + require.NoError(t, err) + _, err = session.Exec(statement) + var pgerr *pq.Error + blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03" + _ = session.Rollback() + _ = session.Close() + require.True(t, blocked, "an uncooperative writer escaped the actual table lock") + } + releaseLock() + select { + case err := <-done: + require.NoError(t, err) + case <-time.After(5 * time.Second): + t.Fatal("transaction did not finish") + } + for _, statement := range statements { + _, err := raw.Exec(statement) + require.NoError(t, err) + } + }) + run("concurrent-preparation-has-one-owner", func(t *testing.T) { + start, done := make(chan struct{}), make(chan error, 2) + for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} { + go func(id string) { + <-start + _, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource()) + done <- err + }(id) + } + close(start) + ok, conflict := 0, 0 + for range 2 { + select { + case err := <-done: + if err == nil { + ok++ + } else if errors.Is(err, auth.ErrSourceOperationConflict) { + conflict++ + } else { + t.Fatal("unexpected concurrent preparation error") + } + case <-time.After(5 * time.Second): + t.Fatal("concurrent preparation timed out") + } + } + require.Equal(t, 1, ok) + require.Equal(t, 1, conflict) + }) + }) + } +} + +func TestSourceOperationPostgresProcessRecovery(t *testing.T) { + step := os.Getenv("CUSTODIAN_PG_STEP") + require.NotEmpty(t, step, "explicit synthetic recovery phase required") + ctx, engine, _ := postgresOperationDB(t, "custodian_recovery") + switch step { + case "prepare": + resetPostgresOperations(t, engine) + _, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource()) + require.NoError(t, err) + case "crash": + engine.AddHook(operationCrashAfterSourceInsert{}) + _, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + t.Fatal("actual source insert did not reach the process-death hook") + case "recover-prepared": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + _, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-applied": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.EqualValues(t, 1, n) + _, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-inverse": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + default: + t.Fatal("unknown synthetic recovery phase") + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test_build2.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test_build2.go new file mode 100644 index 0000000..38229b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/source_operation_postgres_test_build2.go @@ -0,0 +1,314 @@ +//go:build source_operation_postgres + +package auth_test + +import ( + "context" + "errors" + "os" + "sync" + "testing" + "time" + + auth "forgejo.org/models/auth" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + oauth2 "forgejo.org/services/auth/source/oauth2" + + "github.com/lib/pq" + "github.com/stretchr/testify/require" + "xorm.io/xorm" +) + +// The real Forgejo engine/driver, registered model schemas and public operation +// functions run. Only the database, application configuration and actors are +// synthetic. No deployed owner database or authenticated operation is claimed. +func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) { + t.Helper() + require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required") + oldSettings, oldContext := setting.Database, db.DefaultContext + oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine) + require.True(t, ok) + expectedSchema := schema + if expectedSchema == "" { + expectedSchema = "public" + } + raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+expectedSchema) + require.NoError(t, err) + _, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + expectedSchema + `"`) + require.NoError(t, err) + setting.Database.Type = "postgres" + setting.Database.Host = "/run/source-operation-postgres" + setting.Database.Name = "custodian_source" + setting.Database.User = "custodian_test" + setting.Database.Passwd = "" + setting.Database.Schema = schema + setting.Database.SSLMode = "disable" + setting.Database.LogSQL = false + setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2 + setting.Database.SlowQueryThreshold = 0 + require.NoError(t, db.InitEngine(context.Background())) + engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) + require.True(t, ok) + t.Cleanup(func() { + _ = engine.Close() + _ = raw.Close() + setting.Database = oldSettings + db.SetDefaultEngine(oldContext, oldEngine) + }) + version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema") + require.NoError(t, err) + require.Equal(t, "16", string(version[0]["major"])) + require.Equal(t, expectedSchema, string(version[0]["schema"])) + require.NoError(t, db.SyncAllTables()) + auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) + return db.DefaultContext, engine, raw +} + +func resetPostgresOperations(t *testing.T, engine *xorm.Engine) { + t.Helper() + _, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`) + require.NoError(t, err) + _, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`) + require.NoError(t, err) +} + +func TestSourceOperationPostgres(t *testing.T) { + for _, profile := range []struct{ name, schema string }{{"default", ""}, {"public", "public"}, {"named", "custodian_named"}} { + t.Run(profile.name, func(t *testing.T) { + ctx, engine, raw := postgresOperationDB(t, profile.schema) + run := func(name string, f func(*testing.T)) { + t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) }) + } + run("create-reconcile-inverse", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource()) + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + require.Positive(t, r.SourceID) + post := operationRows(t, ctx) + again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, r, again) + require.Equal(t, post, operationRows(t, ctx)) + _, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + require.Equal(t, before, operationRows(t, ctx)) + _, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + }) + run("exact-raw-preexisting-restoration", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" + _, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + _, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + run("unchanged-preexisting-survives", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, "unchanged", r.Phase) + _, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + for _, stage := range []string{"before-apply", "before-inverse"} { + run("private-drift-"+stage, func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s) + require.NoError(t, err) + if stage == "before-inverse" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + require.NoError(t, err) + } + _, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + if stage == "before-apply" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } else { + _, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + }) + } + for _, kind := range []string{"user", "external"} { + run("linked-"+kind+"-refuses-deletion", func(t *testing.T) { + _, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource()) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.NoError(t, err) + if kind == "user" { + _, err = engine.Insert(&user_model.User{ID: 1, LowerName: "synthetic", Name: "synthetic", Email: "synthetic@invalid.test", LoginSource: r.SourceID}) + } else { + _, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID) + } + require.NoError(t, err) + before := operationRows(t, ctx) + _, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.True(t, auth.IsErrSourceInUse(err)) + require.Equal(t, before, operationRows(t, ctx)) + var count int64 + if kind == "user" { + count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count() + } else { + count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count() + } + require.NoError(t, err) + require.EqualValues(t, 1, count) + }) + } + run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource()) + require.NoError(t, err) + _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`) + require.NoError(t, err) + _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + }) + run("uncooperative-writers-excluded", func(t *testing.T) { + locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1) + var once sync.Once + releaseLock := func() { once.Do(func() { close(release) }) } + defer releaseLock() + go func() { + done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil }) + }() + select { + case <-locked: + case err := <-done: + t.Fatalf("lock acquisition refused: %v", err) + case <-time.After(5 * time.Second): + t.Fatal("lock acquisition timed out") + } + statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`} + for _, statement := range statements { + session := raw.NewSession() + require.NoError(t, session.Begin()) + _, err := session.Exec(`SET LOCAL lock_timeout='150ms'`) + require.NoError(t, err) + _, err = session.Exec(statement) + var pgerr *pq.Error + blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03" + _ = session.Rollback() + _ = session.Close() + require.True(t, blocked, "an uncooperative writer escaped the actual table lock") + } + releaseLock() + select { + case err := <-done: + require.NoError(t, err) + case <-time.After(5 * time.Second): + t.Fatal("transaction did not finish") + } + for _, statement := range statements { + _, err := raw.Exec(statement) + require.NoError(t, err) + } + }) + run("concurrent-preparation-has-one-owner", func(t *testing.T) { + start, done := make(chan struct{}), make(chan error, 2) + for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} { + go func(id string) { + <-start + _, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource()) + done <- err + }(id) + } + close(start) + ok, conflict := 0, 0 + for range 2 { + select { + case err := <-done: + if err == nil { + ok++ + } else if errors.Is(err, auth.ErrSourceOperationConflict) { + conflict++ + } else { + t.Fatal("unexpected concurrent preparation error") + } + case <-time.After(5 * time.Second): + t.Fatal("concurrent preparation timed out") + } + } + require.Equal(t, 1, ok) + require.Equal(t, 1, conflict) + }) + }) + } +} + +func TestSourceOperationPostgresProcessRecovery(t *testing.T) { + step := os.Getenv("CUSTODIAN_PG_STEP") + require.NotEmpty(t, step, "explicit synthetic recovery phase required") + ctx, engine, _ := postgresOperationDB(t, "custodian_recovery") + switch step { + case "prepare": + resetPostgresOperations(t, engine) + _, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource()) + require.NoError(t, err) + case "crash": + engine.AddHook(operationCrashAfterSourceInsert{}) + _, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + t.Fatal("actual source insert did not reach the process-death hook") + case "recover-prepared": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + _, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-applied": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.EqualValues(t, 1, n) + _, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-inverse": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + default: + t.Fatal("unknown synthetic recovery phase") + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/tracing_context_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/tracing_context_test.go new file mode 100644 index 0000000..56d34d5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/preimages/tracing_context_test.go @@ -0,0 +1,53 @@ +package db + +import ( + "bytes" + "context" + "runtime/trace" + "testing" + + "xorm.io/xorm" + "xorm.io/xorm/contexts" +) + +type followingQueryHook struct{} + +func (followingQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + return c.Ctx, nil +} +func (followingQueryHook) AfterProcess(*contexts.ContextHook) error { return nil } + +func TestTracingContextSurvivesFollowingHooks(t *testing.T) { + for _, sensitive := range []bool{false, true} { + for _, fail := range []bool{false, true} { + engine, err := xorm.NewEngine("sqlite3", ":memory:") + if err != nil { + t.Fatal(err) + } + engine.AddHook(TracingHook{}, followingQueryHook{}) + ctx := context.Background() + if sensitive { + ctx = WithSensitiveSQL(ctx) + } + var output bytes.Buffer + if err := trace.Start(&output); err != nil { + t.Fatal(err) + } + func() { + defer trace.Stop() + query := "SELECT ?" + if fail { + query = "SELECT missing_synthetic_function(?)" + } + _, err = engine.Context(ctx).Exec(query, "synthetic-following-hook-sentinel") + }() + _ = engine.Close() + if (err != nil) != fail { + t.Fatal("unexpected real SQLite query outcome") + } + if bytes.Contains(output.Bytes(), []byte("synthetic-following-hook-sentinel")) == sensitive { + t.Fatal("following hook changed private or ordinary trace disclosure") + } + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/public-database-image.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/public-database-image.json new file mode 100644 index 0000000..6e46776 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/public-database-image.json @@ -0,0 +1,8 @@ +{ + "image_id": "sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "repo_digests": [ + "postgres@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777" + ], + "selector": "existing local postgres:16-alpine", + "target_major": "PostgreSQL 16 in current Forgejo database source" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/binary-version-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/binary-version-result.json new file mode 100644 index 0000000..babfeb8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/binary-version-result.json @@ -0,0 +1,57 @@ +{ + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--dir", + "/app", + "--ro-bind", + "/tmp/custodian-forgejo-release-r4/source/gitea", + "/app/forgejo", + "--bind", + "/tmp/custodian-forgejo-release-r4/embedded-extracted", + "/results", + "--chdir", + "/tmp", + "--clearenv", + "--setenv", + "PATH", + "/nonexistent", + "--setenv", + "HOME", + "/tmp/home", + "--setenv", + "USER", + "builder", + "--setenv", + "USERNAME", + "builder", + "--setenv", + "GITEA_WORK_DIR", + "/tmp/work", + "--setenv", + "GITEA_CUSTOM", + "/tmp/custom", + "--setenv", + "GITEA_APP_INI", + "/tmp/absent-app.ini", + "--setenv", + "TZ", + "UTC", + "--setenv", + "LC_ALL", + "C", + "--", + "/app/forgejo", + "--version" + ], + "phase": "binary-version", + "returncode": 0, + "seconds": 0.16 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/build.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/build.json new file mode 100644 index 0000000..134cab3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/build.json @@ -0,0 +1,7 @@ +{ + "binary_sha256": "92efb26614d3fca22c400920d92c4cd022e32ab86c1f562f60699ab5f750cd28", + "frontend_built": true, + "image_deployment_selected": false, + "source_locks_preserved": true, + "tags": "bindata timetzdata sqlite sqlite_unlock_notify" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-attempts.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-attempts.json new file mode 100644 index 0000000..78b0c95 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-attempts.json @@ -0,0 +1,708 @@ +[ + { + "operation": [ + "migrate" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused-debug-logging", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/CLIENT_SECRET" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "synthetic-oauth", + "--provider", + "github", + "--key", + "synthetic-public-id", + "--secret-file", + "/run/forgejo-oidc/CLIENT_SECRET", + "--group-claim-name", + "groups", + "--admin-group", + "unchanged-admin", + "--scopes", + "read:user,user:email" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--icon-url", + "https://synthetic.invalid/icon" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--secret-file", + "/run/forgejo-oidc/replacement" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "synthetic-guarded" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--secret-file", + "/run/forgejo-oidc/good" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/newline" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/newline" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/short" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/short" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/long" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/long" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/badbyte" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/badbyte" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/world" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/world" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/writable" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/writable" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/executable" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/executable" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/empty" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/empty" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/symlink" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/symlink" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/fifo" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/fifo" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/directory" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/directory" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/missing" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/missing" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/badparent/value" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/badparent/value" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/parentlink/value" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/parentlink/value" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "relative" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "relative" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/forgejo-oidc/../forgejo-oidc/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/forgejo-oidc/../forgejo-oidc/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--provider", + "github", + "--secret-file", + "/run/writable-input/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--name", + "refused", + "--secret-file", + "/run/writable-input/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "add-oauth", + "--name", + "refused", + "--secret", + "", + "--secret-file", + "/run/forgejo-oidc/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--secret", + "", + "--secret-file", + "/run/forgejo-oidc/good" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--required-claim-values", + "" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--secret-file", + "/run/forgejo-oidc/replacement" + ], + "returncode": 1, + "stdout_stderr_secret_absent": true + }, + { + "operation": [ + "admin", + "auth", + "update-oauth", + "--id", + "1", + "--secret", + "synthetic-legacy-literal" + ], + "returncode": 0, + "stdout_stderr_secret_absent": true + } +] \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-results.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-results.json new file mode 100644 index 0000000..bb076dc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/cli-results.json @@ -0,0 +1,13 @@ +{ + "passed": true, + "subprocesses": 48, + "real_backend_cli_and_sqlite": true, + "network": "disabled by bwrap", + "secret_inputs": "synthetic only", + "private_file_values_never_in_argv_env_output": true, + "actual_browser_login": false, + "actual_readonly_file_bind": true, + "replacement_preserves_other_config": true, + "unsafe_SQL_logging_refused": true, + "actual_error_and_slow_hooks_no_leak": true +} \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build-result.json new file mode 100644 index 0000000..8da5f41 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build-result.json @@ -0,0 +1,24 @@ +{ + "argv": [ + "docker", + "buildx", + "build", + "--output", + "type=oci,dest=/tmp/custodian-forgejo-release-r4/image.oci.tar,rewrite-timestamp=true", + "--no-cache", + "--platform", + "linux/amd64", + "--pull=false", + "--network=none", + "--provenance=false", + "--sbom=false", + "--metadata-file", + "/tmp/custodian-forgejo-release-r4/image-build-metadata.json", + "--build-arg", + "SOURCE_DATE_EPOCH=1752120742", + "/tmp/custodian-forgejo-release-r4/container-context" + ], + "phase": "container-build", + "returncode": 0, + "seconds": 26.78 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build.log new file mode 100644 index 0000000..17026f6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-build.log @@ -0,0 +1,40 @@ +#0 building with "default" instance using docker driver + +#1 [internal] load build definition from Dockerfile +#1 transferring dockerfile: 465B done +#1 DONE 0.0s + +#2 [internal] load metadata for code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b +#2 DONE 0.4s + +#3 [internal] load .dockerignore +#3 transferring context: 2B done +#3 DONE 0.0s + +#4 [internal] load build context +#4 ... + +#5 [1/2] FROM code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b +#5 resolve code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b 0.1s done +#5 CACHED + +#4 [internal] load build context +#4 transferring context: 111.05MB 1.4s done +#4 DONE 1.4s + +#6 [2/2] COPY --chown=0:0 --chmod=755 gitea /app/gitea/gitea +#6 DONE 0.4s + +#7 exporting to oci image format +#7 exporting layers +#7 exporting layers 11.2s done +#7 rewriting layers with source-date-epoch 1752120742 (2025-07-10 04:12:22 +0000 UTC) +#7 rewriting layers with source-date-epoch 1752120742 (2025-07-10 04:12:22 +0000 UTC) 6.9s done +#7 exporting manifest sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7 0.0s done +#7 exporting config sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b 0.0s done +#7 sending tarball +#7 sending tarball 5.7s done +#7 DONE 23.9s + +#8 resolving provenance for metadata file +#8 DONE 0.0s diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-reproduction.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-reproduction.json new file mode 100644 index 0000000..e812d34 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-reproduction.json @@ -0,0 +1,13 @@ +{ + "two_fresh_no_cache_OCI_exports_exact": true, + "compared_fields": { + "image_manifest_digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "config_digest": "sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b", + "oci_index_sha256": "0f283c23a4c7fb461a4f7a745e8bfd95f8def8e67772bbbae43b781e61e92ed7", + "archive_sha256": "499ef48172540b07f01de62e0c78f5798fe2793cac73d0c6188b37c07077e7d0" + }, + "same_verified_binary": true, + "second_source_build_claimed": false, + "registry_publication": false, + "native_admission": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-result.json new file mode 100644 index 0000000..2bf326a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/container-result.json @@ -0,0 +1,17 @@ +{ + "all_base_layers_and_rootless_runtime_config_preserved": true, + "all_oci_blob_digests_verified": true, + "archive_sha256": "499ef48172540b07f01de62e0c78f5798fe2793cac73d0c6188b37c07077e7d0", + "artifact_path": "/tmp/custodian-forgejo-release-r4/image.oci.tar", + "config_digest": "sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b", + "container_epoch": 1752120742, + "deployment_tag": null, + "final_layer_binary_exact": true, + "host_or_secret_mounts": false, + "image_manifest_digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "local_runtime_image_loaded": false, + "oci_index_sha256": "0f283c23a4c7fb461a4f7a745e8bfd95f8def8e67772bbbae43b781e61e92ed7", + "privileged_containers": false, + "registry_publication": false, + "source_epoch": 1752105237 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend-result.json new file mode 100644 index 0000000..50d32a2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend-result.json @@ -0,0 +1,16 @@ +{ + "argv": [ + "make", + "GOFLAGS=-trimpath -buildvcs=false -p=4", + "RELEASE_VERSION=11.0.3+cust80.feb10793", + "FORGEJO_VERSION=11.0.3+cust80.feb10793", + "FORGEJO_VERSION_API=11.0.3+cust80.feb10793", + "TAGS=bindata timetzdata sqlite sqlite_unlock_notify", + "go-check", + "generate-backend", + "static-executable" + ], + "phase": "embedded-and-static-backend", + "returncode": 0, + "seconds": 60.14 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend.log new file mode 100644 index 0000000..731cb4b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-and-static-backend.log @@ -0,0 +1,9 @@ +Running go generate... +generating bindata for migration +generating bindata for options +generating bindata for public +generating bindata for templates +CGO_CFLAGS="-O2 -g -DSQLITE_MAX_VARIABLE_NUMBER=32766" /tmp/custodian-forgejo-release-r4/go/bin/go build -trimpath -buildvcs=false -p=4 -tags 'netgo osusergo bindata timetzdata sqlite sqlite_unlock_notify' -ldflags '-linkmode external -extldflags "-static" -s -w -X "main.ReleaseVersion=11.0.3+cust80.feb10793" -X "main.MakeVersion=GNU Make 4.3" -X "main.Version=11.0.3+cust80.feb10793" -X "main.Tags=bindata timetzdata sqlite sqlite_unlock_notify" -X "main.ForgejoVersion=11.0.3+cust80.feb10793"' -o gitea +# forgejo.org +/usr/bin/ld: /tmp/go-link-3904455077/000012.o: in function `unixDlOpen': +/_/github.com/mattn/go-sqlite3@v1.14.28/sqlite3-binding.c:45328:(.text+0x7a2d): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-extract-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-extract-result.json new file mode 100644 index 0000000..7940511 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-extract-result.json @@ -0,0 +1,62 @@ +{ + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--dir", + "/app", + "--ro-bind", + "/tmp/custodian-forgejo-release-r4/source/gitea", + "/app/forgejo", + "--bind", + "/tmp/custodian-forgejo-release-r4/embedded-extracted", + "/results", + "--chdir", + "/tmp", + "--clearenv", + "--setenv", + "PATH", + "/nonexistent", + "--setenv", + "HOME", + "/tmp/home", + "--setenv", + "USER", + "builder", + "--setenv", + "USERNAME", + "builder", + "--setenv", + "GITEA_WORK_DIR", + "/tmp/work", + "--setenv", + "GITEA_CUSTOM", + "/tmp/custom", + "--setenv", + "GITEA_APP_INI", + "/tmp/absent-app.ini", + "--setenv", + "TZ", + "UTC", + "--setenv", + "LC_ALL", + "C", + "--", + "/app/forgejo", + "embedded", + "extract", + "--include-vendored", + "--destination", + "/results", + "**" + ], + "phase": "embedded-extract", + "returncode": 0, + "seconds": 0.79 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-files.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-files.json new file mode 100644 index 0000000..e618e05 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-files.json @@ -0,0 +1,2236 @@ +{ + "options/gitignore/AL": "sha256:f05c79ca3a46ab4596e639c6aca07ab0e5630fbba53538e225c5c94ad938383e", + "options/gitignore/Actionscript": "sha256:2ef767385125df7d546a79f251e267fab5c0e68a652128987a6982cb61d53584", + "options/gitignore/Ada": "sha256:fdc17fd35182ca77a4888c8682f48ba5b57463ca5865b96eb8a652ba15c63664", + "options/gitignore/Agda": "sha256:5bb6f531b3ed3d665415ba86ac3598d2e0f2d1b40559382155987c0a5acd29c1", + "options/gitignore/Alteryx": "sha256:0c7621db0cf4cd2a5323d85158b01bb7c00645ac1c31094187ca8e1d5fec7d4a", + "options/gitignore/AltiumDesigner": "sha256:b69c9d387c0b553960e45cda6128c60a2770848573896c26de46dd336006945a", + "options/gitignore/Android": "sha256:52b5ae65df42af9c3f73e061858edcaf432b0bde46924b9257bda6a5258b3934", + "options/gitignore/Anjuta": "sha256:6ee69a700f0975f8f0545564da73f3ac0c46e9e5c5b3cc807851f2a3e9325006", + "options/gitignore/Ansible": "sha256:b2f4cbbad2c887f83a2aeff06e55ac683c4fabdf460b41ae441e658bdce38288", + "options/gitignore/AppEngine": "sha256:cc64f9dffe4cbe9c88c0050bbb5d3e00f25d4b860ad0347a634f6c77a7dffe6f", + "options/gitignore/AppceleratorTitanium": "sha256:0c59c62ff626d77fd48d94eec2ee476d9057938769e5a12f60a2afdaf16a071e", + "options/gitignore/ArchLinuxPackages": "sha256:ba859633c082e8318f606d4bf358f7d1c42d8da63ecea468edd4bbef5867b706", + "options/gitignore/Archives": "sha256:8aad38dc53bfe640bb6253b78fab36f0382addd6f82262bfea59c6d4064b95a4", + "options/gitignore/AtmelStudio": "sha256:e078e7bc5da553129b90f9f4caf1d645af48303b4d84713345c7ea9da12369ae", + "options/gitignore/AutoIt": "sha256:4f7b3b8cb293e53162d4bc548d1f9250381d48c5b0f76513bca5f7d25bb6f136", + "options/gitignore/Autotools": "sha256:9541801d55b762f180a5ee18fdc493df5e15389cd3066f6537f96004babe497c", + "options/gitignore/B4X": "sha256:721fddee433c8d3a2cb71f6d27b32d80392071188a642e5de53f48bce984569b", + "options/gitignore/Backup": "sha256:d22a966b3a683594e214af3a6930f79eb072d4f362f0a7869ff4b861b6b76c4d", + "options/gitignore/Ballerina": "sha256:0540c8ab82f25b47e870226465a2add1391e4acfd7433971bafe5e0712be5cea", + "options/gitignore/Bazaar": "sha256:cdfbf2819d1d19a81820313a7b05f28b1266ebbbfb9fc75cce2b8f174e28d168", + "options/gitignore/Bazel": "sha256:2051025fe271d356307df6af5fd8b5d2ae7b875350dc994af7752ca5160600f2", + "options/gitignore/Beef": "sha256:770be56b38a29d30888b8c8e1c83c91b67cbf2a9a25c8d4341313bb3e4f5e4c7", + "options/gitignore/Bitrix": "sha256:f5193a00db0d0958d17501d1fbaa618703af98943a1585fbc576ec7c05cd1998", + "options/gitignore/BricxCC": "sha256:40255dbd217dbf47c27f32a5439985c416e4b00bda54bf2ab8f74998ad200630", + "options/gitignore/C": "sha256:29a3db423bf9d10e68f8950af557ddb891f407d262a8286511fb22df48ef458f", + "options/gitignore/C++": "sha256:3ba2020da55f76620d61950cb88f0d6cc0d5852eae7b2c03f8487a64c87da959", + "options/gitignore/CDK": "sha256:552c05634903863e77cbaf340d5300b0751450063fb50f5e13d9ea48f1938a0a", + "options/gitignore/CFWheels": "sha256:bc08e26b7032c3069fdca1293cb83b6b89d011fb8f5b67925aacb0668fd8d131", + "options/gitignore/CMake": "sha256:be2f29483a5dbce0168b535d8a274d6ab93bee6a9b026f4efba9acfdfefdcd6e", + "options/gitignore/CUDA": "sha256:b0980343aa1a40beba306fe9db6644a5268ab383b4544afccb9c3fd346d0b7be", + "options/gitignore/CVS": "sha256:8ee770eecf8c5703855463c53f78fbfd73aa0c9a2e0013a5bb9aca954625ac17", + "options/gitignore/CakePHP": "sha256:49ca50f966ddbcae84f7cacc26e7d4003708f350b2bd433cb53e7063550db660", + "options/gitignore/Calabash": "sha256:94dca798ca9455ed23426aa794b2908d2b1815647e75254f4e3e53d6d0e833f0", + "options/gitignore/ChefCookbook": "sha256:39eb1c0ba5f79760355a8be70577a0b25c6feb0c33db9a26b2191316934130b6", + "options/gitignore/Clojure": "sha256:d8f9c76ea8787c6a0f96be13b22db2e52fb2acc0857b72304c62abebb6607398", + "options/gitignore/Cloud9": "sha256:ecc67c1f4779fa0e1fb747921db8aeb1d76809668d0625ac9c19d473c715cca1", + "options/gitignore/CodeIgniter": "sha256:8e50c68fe726b71752b55b8eab30d0fe5c3b1cc148ad6346feed9611b3555bd0", + "options/gitignore/CodeKit": "sha256:226bdb0e5d5d015318946c39dfb29d9e2caeff411176146e525521c218ca18cd", + "options/gitignore/CodeSniffer": "sha256:4e5e505059459335762fa8d91179e35f3b569670e1b1f383cc3a227eb57b1fb9", + "options/gitignore/CommonLisp": "sha256:d964edefd4325ae4533c87efdc8295981d09f61ef60d10a815e2baedf5f5d40e", + "options/gitignore/Composer": "sha256:96cf522044b5bb4bde93bf199d15e63d3d7cd400c059fa8237fffceac4f96ccc", + "options/gitignore/Concrete5": "sha256:da82ee4dd11e5011fd74e5c93c546a50901fd4f9f62cd308dcb551df71e23090", + "options/gitignore/Coq": "sha256:3990802f2c9a495a278649fb7c1d1e865de7144ccb7777a9999e193ce7241bb8", + "options/gitignore/Cordova": "sha256:e9fef45c976a633cfd1d66b44c787df16ebbdbd68d9e80716c7062a54395f6b8", + "options/gitignore/CraftCMS": "sha256:6897f4cc9cb19773ede6ee754c851765e8511ce50a832916749c0ed734ee5faa", + "options/gitignore/D": "sha256:457bfa3e9d27098697ebd7beb629f52668fa4f911044ddbd61f026db59bcf809", + "options/gitignore/DM": "sha256:8e6216c7fbde93eec3ff67dae438c2066b35b3b88c943ac78a1a40e001a823c0", + "options/gitignore/Dart": "sha256:cd3ce4936a89020fa89c968314b7e877fa5635053c274bba4a7f3dca65f6ee17", + "options/gitignore/DartEditor": "sha256:d6c5a837d9bedf53b0748a40bc70cd5041555384e08436ae57b4f25658d8fd11", + "options/gitignore/Delphi": "sha256:1f387131d4c2f0bac81ed52633434416e7e8e33cd554bcb1f35945365cb7af7c", + "options/gitignore/Diff": "sha256:d492b6a308cc5317e8380b676fe52811b009139e0038d0efee7c505b0ae3a24e", + "options/gitignore/Dreamweaver": "sha256:8240387a57377b39ef8016e5a8d54358047fc380cca9c933e7d855ee7e53cfce", + "options/gitignore/Dropbox": "sha256:0c05169abc65e8859d4232e92a9d613b3025aa1b773dc78aa3de46d9ce31a7bd", + "options/gitignore/Drupal": "sha256:44a44f31b3f71fe42bf42d084bbc8c70aae03cf53c6df0bebd739ac1fef9e05c", + "options/gitignore/Drupal7": "sha256:a3c043643b44d0ea74dd349ff57a452bde6a0ef7ef9941f9d9ba8bf3b5f846a2", + "options/gitignore/EPiServer": "sha256:66d82b275df3a8be3c8d6c16c1386a5352594529b284c483d8955c80250734d6", + "options/gitignore/Eagle": "sha256:d84713300f7c7549c032268ebca1a1dc64bacb5393c349638e3660329fd30eab", + "options/gitignore/Eclipse": "sha256:4fa08de567e833e4c1a193f1b893ef7bd2a5ef9cd2f3ae8b8e0193c007a4f9c0", + "options/gitignore/EiffelStudio": "sha256:943ddf79c472ed492446367a53b26e3742604a430681db521dfc461b5c0ccdba", + "options/gitignore/Elisp": "sha256:1290636c885835e80ae2bab07c35694be8a4cd9aa9345054f924676833036a92", + "options/gitignore/Elixir": "sha256:9aa07ac3f74cdf6413b271baf7e586901f217e4a8886d11f1e7461c0d4d3a4b0", + "options/gitignore/Elm": "sha256:973a5c0d1a9a47f93feab0399a7e5c2d47460771edd88fe9eb6f9f92a6697392", + "options/gitignore/Emacs": "sha256:57ab403f6372c4b59c54e87f82742d341415163cd28fe393570a40cf770cbb2e", + "options/gitignore/Ensime": "sha256:745c912069e1ee2c0748c72c79f9124e5c7d617e764eaecc942108198a964a02", + "options/gitignore/Erlang": "sha256:43e3065303fa4c2943142d922f5e0629866108f73bd1e91743a50c082238527d", + "options/gitignore/Espresso": "sha256:0718e158e82a28a290fad24c68bc5d34b541dc4f5a3168f1b071a984af57d69f", + "options/gitignore/Exercism": "sha256:913d47dc0c650fc2654e0fab0dee1bf5bdee30ba0e55045cb2f889d12948a78d", + "options/gitignore/ExpressionEngine": "sha256:ef0f83e36dbb94c293414fd38e78261e0d97c2ff525c612b59ebbe0e6e6d5d4c", + "options/gitignore/ExtJs": "sha256:19064caf2b9198c361a6e4e4aab9d01b8bba49a186ca1bd05c1dd3fbffa5c762", + "options/gitignore/Fancy": "sha256:bc8873c86a281e69aa1d5e8a243e47b1d62accc351cb5355a15e2259ac1b9546", + "options/gitignore/Finale": "sha256:c18d93db34f07648b4f77472d4ed18e99d5641c40a2d070546a5bdb9964c8f81", + "options/gitignore/FlaxEngine": "sha256:b050079c7895eeb4862c1aedd6fbb96850d6800995542a042c72c7bada097eec", + "options/gitignore/FlexBuilder": "sha256:62d618d113f3d37bf50e1eea54a805009af607dac7e76e06e53ef981a0f9c7e1", + "options/gitignore/Flutter": "sha256:6141695de3693ec86a541367902db3b377f47fbccdfb7f95b56fb78be398b6c0", + "options/gitignore/ForceDotCom": "sha256:e94321a202166e303a54a84d6e3f15a9a4d60860fd794c2264da7e58c2024b86", + "options/gitignore/Fortran": "sha256:3ba2020da55f76620d61950cb88f0d6cc0d5852eae7b2c03f8487a64c87da959", + "options/gitignore/FuelPHP": "sha256:975eccb69b0e09df9b3395f62c69f9d435ae82eacd53f6bf6bb59080d19ef439", + "options/gitignore/GNOMEShellExtension": "sha256:b9dc032275694b3fe307e679fd83ae98f69bbe6f66d12a7e50a97c11f8044235", + "options/gitignore/GPG": "sha256:9d48baf52b029262f4a0d8e9aadb366fa4f3b6837c68d2a792beb6a4fa85463d", + "options/gitignore/GWT": "sha256:cb3dd02b3cd87fb649ee7306760879177d1d739118ebbf9f4b19e00271dcadd3", + "options/gitignore/Gcov": "sha256:bf764688c2a7defd7ff5e82c6fd93d1e4678864e3c5f4ac54a70b85a0a74da2c", + "options/gitignore/GitBook": "sha256:63afe166951a7eea420b168273d1b9c8a872149406eb7324620548cebadad011", + "options/gitignore/GitHubPages": "sha256:021646a73693cc70de07d4bd29c4872bc78c596a482ca43b714cca10c8aed1c1", + "options/gitignore/Go": "sha256:b319f85e4a246c38474a242ecaee46ca514c4abcfae781f0f7e2a7a58b3e5a4f", + "options/gitignore/Go.AllowList": "sha256:d725fdb6b0f5050c989269a423bafbb1856ba4a0e8fd0d6aa9f60d066738fa09", + "options/gitignore/Godot": "sha256:44c82a7a02ebb093fc186425e60f0f779bb130517372be96fd871df384e7e387", + "options/gitignore/Gradle": "sha256:b71781ce2a66e30262b49250d39c58a3a332dabd2817eba8672e64db44034a85", + "options/gitignore/Grails": "sha256:9b68f67ab15514b44062309d9bc6a29db64c797df22cfd766ab3eb56d4e1b061", + "options/gitignore/Gretl": "sha256:f8046b37ce6f35c8c2faafd0a43f32889d416570806d41ed450a63aa3f4d6d61", + "options/gitignore/Haskell": "sha256:4e2c01e708551c842fc55d1b2cd9f3b629f743989cd9ec91d16b6ebb01ff0b63", + "options/gitignore/Hexo": "sha256:a2c6c2d7cc894f7cb61409c5a1875022da5dfbf64969398cd629dea8e2d030d4", + "options/gitignore/Hugo": "sha256:40c3ebd49119adc242c9813a7d7ea3caf5da76dcc299303e9ae79c30a63bb246", + "options/gitignore/IAR": "sha256:8a021581b7d64719baa4685a21e007f87d28f202d30084c1b3abcf20dec58512", + "options/gitignore/IAR_EWARM": "sha256:3edee1a1b3099cff15e46281e6579c52c566b86c8587d3f26c510a45742c4fb6", + "options/gitignore/IGORPro": "sha256:79623a032c9c3a8055f61b3fddb163a0bd8d2aa8487032d06f8d09eb92854de9", + "options/gitignore/Idris": "sha256:14cbce7796961f3dc681f5f69e50cc658c7b3f67ede6908d3e234f5462705933", + "options/gitignore/Images": "sha256:d941bab9f6e01b90739ded50876dafeba0e6ff695aa60ea9ed7235d9ae4f7519", + "options/gitignore/InforCMS": "sha256:4a22267507e01f481746b0c26d2e6de563cde8c4c5d663a6fe9096649754d341", + "options/gitignore/JBoss": "sha256:1b358ced734a287dd400aac524d4b31a64f04f331cd01d533db766a278a2e2c2", + "options/gitignore/JBoss4": "sha256:69deb3f028cc81730f07ff7f27c00ee0dcfaa7847f9f7219c15c2a9cbaae68ec", + "options/gitignore/JBoss6": "sha256:831c7a4774d6d7688a1720de33a84a0b66229c98978d9429205b45d7ab9bd5ad", + "options/gitignore/JDeveloper": "sha256:674c8d8b62409a8e894b6f1d1704ac844d38c8479f7b9d089ab40be3d1da875f", + "options/gitignore/JENKINS_HOME": "sha256:60c0c83ee53a9e30c2b28a623ddf2e9818666d6ce4e69e5d393cecf41ce84c9b", + "options/gitignore/JEnv": "sha256:1fc0bbe69599b35768cf0c3638aac89d46a300cddb00682c8118b5191d2c96d1", + "options/gitignore/Janet": "sha256:2a8cc9fac9e77163f3a7926986b9c457d58c4ab34d19ffd407c3f9355f8154c5", + "options/gitignore/Java": "sha256:affbdc2a83b7d656b98cb2f77f6c33e8ce8221919ea1a50d803b19647588f07d", + "options/gitignore/Jekyll": "sha256:d52af0f80b768a627352ce3da91ad5af1396c77964587570d42b23fd9683bae6", + "options/gitignore/JetBrains": "sha256:a00597564e1f7dae6e6fd16920100621e392c0f3264cb16bb115fef5be4d873b", + "options/gitignore/Jigsaw": "sha256:070208c9d20e37e749d13bca38a0b48aae4c436939f696f7ed1b1b5ddc7b32e7", + "options/gitignore/Joomla": "sha256:0accfe4e93e78ee6d35193ed3becca4a261e25064cd83e5cdca42fb0246b8934", + "options/gitignore/Julia": "sha256:7fed89ffed08fa8724e06b8f8cbc2ca9cfd42c5b4b99ec13f83c86fc42c4cca0", + "options/gitignore/JupyterNotebooks": "sha256:6915b3929ff2ccfa263a1bf35c0508867ea6a9f251aa512c29f2b3f930331b99", + "options/gitignore/KDevelop4": "sha256:c79b22f32907b9a18228eb366b1dbe49e45ecc2359f729e2b1d6f40edf572375", + "options/gitignore/Kate": "sha256:237cd7f4104698428ff8b9c0af4d86b7f8db127fa3496ae4250f55ffe7eaff31", + "options/gitignore/Kentico": "sha256:727f6486fb19596e7ec7217ec5df7b3d724829fff4a98a90c484ac12746bc7ff", + "options/gitignore/KiCad": "sha256:b701ee6a38b8771d2bd36acc40469557a264961c61bb47a5890b769b800caad0", + "options/gitignore/Kohana": "sha256:3316bc17d92fad835a9847f61f038c69a94b58d8ff86ec0d54a8004d45ac2241", + "options/gitignore/Kotlin": "sha256:affbdc2a83b7d656b98cb2f77f6c33e8ce8221919ea1a50d803b19647588f07d", + "options/gitignore/LabVIEW": "sha256:8f7268d22d6bf29ff0f07a2cd256d4de1c13fae0e746b5f8f6527e55fef3b294", + "options/gitignore/Laravel": "sha256:27d568e98e875bde7d378460213cb0dd37b715cb4139c05694ab3c05e5de1a9b", + "options/gitignore/Lazarus": "sha256:82eb3a7605fcbba0ebb728a4f3c9e327117963e04767db06743658da7628a974", + "options/gitignore/Leiningen": "sha256:d8f9c76ea8787c6a0f96be13b22db2e52fb2acc0857b72304c62abebb6607398", + "options/gitignore/LemonStand": "sha256:d5e5bedabaf49e7a89de460596c3bdd592a560d7c7e759fcb3e5275c6e55d1ac", + "options/gitignore/LensStudio": "sha256:364a12efc0b87e603245269d9d6c78040104e8374d9f974ca49502d2e7bcea06", + "options/gitignore/LibreOffice": "sha256:63fefb726ed0bc33b42497a50c6c2491b9571ca403f4e8d0904480f12b3a922f", + "options/gitignore/Lilypond": "sha256:3a66557fa3384aee22fef8cb539533658ccb59de3d1a4d9875a2829c7c9e7a99", + "options/gitignore/Linux": "sha256:90124d99a301e3c33d4c375508285de7ce2d91e6ec4747987c95fd6918c95728", + "options/gitignore/Lithium": "sha256:c574be837446fbb85ea87893b68525dcaee1aa6ba0706de6d108e025cc72118a", + "options/gitignore/Logtalk": "sha256:b30a5831725de1a0f4417fbdff84700a31edc3faee9d65344997ea67834c4015", + "options/gitignore/Lua": "sha256:8fdf21d2124e9e1497c332631b1cdae3b9431bb111c7ba87c922af455a5c3a79", + "options/gitignore/LyX": "sha256:d02ef0803105dd85030432c44e36d2e84c756510271af500608b31c3745d3438", + "options/gitignore/MATLAB": "sha256:1fd2720828c2a922d84b53b269fe17364c4496d740cdf6b5b1df8cecdb859752", + "options/gitignore/Magento": "sha256:04a39e494d0b7799912bc4f1c9f68432b3f2ab0b2a0bdb4234c37377a7cd4cf5", + "options/gitignore/Magento1": "sha256:7e967b7f761d7327e6cd7d2d9ec43e4330d40f1ce290d0ad8071626edfb097e5", + "options/gitignore/Magento2": "sha256:731a9ce80a9a8efbff499f4a66dde69c4ddf5c2884699037238a9734d6962da5", + "options/gitignore/Maven": "sha256:11eb2b7c960e5b98e4e7996df11343804dd6927b999bbc22f91aaa5ea7fd058c", + "options/gitignore/Mercurial": "sha256:075c95c769058a20457169814a9d58e772a8f03fb313cfbbae83bb2b20ab0b62", + "options/gitignore/Mercury": "sha256:95478da1c91ff6c710f9752ab3c8bf063f5b05193603d3b470771971191f1b8d", + "options/gitignore/MetaProgrammingSystem": "sha256:93890e1ffd89841259b189fc9b8bc41565df9ed6395c05ce4a2136d8dcd79e13", + "options/gitignore/Metals": "sha256:b42fc8398de0d6b8943708a4b27c1215c66d1ee95fb099966ea3481def174bae", + "options/gitignore/Meteor": "sha256:27b7a08cf3531bdb9475e3179d2791e5bed9c4ccbf86e953c7e4fb3ebc4d8689", + "options/gitignore/MicrosoftOffice": "sha256:1c1f491dc999f930b88860e794af1f0c2f71335501b64399f6dfcd729f34c492", + "options/gitignore/ModelSim": "sha256:3b5dd3309d56882aa1a10ab7b6406c76eda118d45305483ebce237c7fccae543", + "options/gitignore/Momentics": "sha256:6a84e7704e0f668efa544f7abfc360b1d033825d3616b20d30c36904ebbe63f1", + "options/gitignore/MonoDevelop": "sha256:acf916c2e03d0137b0bbcd47727d2230d7f0a4082d6a8acce36908cd5e8f9ee3", + "options/gitignore/NWjs": "sha256:cb15b31c864062a25fe59f5ab73e1e962fd367db3f963b0c8afefe875600b22c", + "options/gitignore/Nanoc": "sha256:b958a952c8abfefd612542f0e0a258908c37c39d8404aff96e45612e63ffc4c1", + "options/gitignore/NasaSpecsIntact": "sha256:e329a4a482ffd37693029d0eb2ba3b7b60e803827646516100dd0b90e3f2d911", + "options/gitignore/NetBeans": "sha256:aad44c7220a731edcb93455dfc43546fcaffd55a9e450ed4cb617b8c55527a47", + "options/gitignore/Nikola": "sha256:394d2d0a37b3dcb5762be7445525b18979fbfb8348dce3173139fadf5f0361fa", + "options/gitignore/Nim": "sha256:266b368f7338301d955d47786f742d5f2136d1c076ddbd64821b73251cceab47", + "options/gitignore/Ninja": "sha256:e7dea831e3b2999c3c5f25884fc0c89f36e3a4705a6fd27e4e60ea3a96c91eb6", + "options/gitignore/Nix": "sha256:f98c41fd24c40f997ee7428abceffda1134427bd67488464a7e29f72cac1e48c", + "options/gitignore/Node": "sha256:bdceec63c8b9f23b0b23c7daaaf0a8e41ba112d5ded9c02cdc5ebc26271eb24f", + "options/gitignore/NotepadPP": "sha256:14213804a5ac6e6fe465065dfc6862f87ebe021b9cffebddd7aa7d28fdc7b6b1", + "options/gitignore/NotesAndCoreConfiguration": "sha256:8456d7cd89e6fac35fc53241697827a5aaa45c6fd334d9d3fb13f370bf4d7567", + "options/gitignore/NotesAndExtendedConfiguration": "sha256:1b48475cd75633758ac083354ebe096589652e3b416045594f9d719b2dc65af6", + "options/gitignore/NotesOnly": "sha256:14063d7540ce2b8a8ca74b6de5b9e5269f4fa5a1015b79a97401679aa97c6eff", + "options/gitignore/OCaml": "sha256:411ac0861a0e60aa721c63c68b22191d4103e90b971a3bfa554e1a6919245213", + "options/gitignore/Objective-C": "sha256:493559871229099670ee7ef65518fab3c5650196c907e4d16b4144797b745aab", + "options/gitignore/Octave": "sha256:1fd2720828c2a922d84b53b269fe17364c4496d740cdf6b5b1df8cecdb859752", + "options/gitignore/Opa": "sha256:4628862c29a7105ec831162de34fb6c5b2c7db0a01f2f08112e6174666259dec", + "options/gitignore/OpenCart": "sha256:df49484a922b5732e4593b94990ced40bd01c531fe1cf01d39b13a9eb713dc90", + "options/gitignore/OpenSSL": "sha256:642dfa3264135da89fba68210782822a86287051973b2b65ff86c043d1d48f22", + "options/gitignore/OracleForms": "sha256:206f4a27f146d557661675742a12d7f927290e7173e2063848a40f8e2e49230a", + "options/gitignore/Otto": "sha256:b4a1f6845dfd5bf2f96de8d72ae7bb196d108c1790238beb52cc80c01ff14a69", + "options/gitignore/PSoCCreator": "sha256:accd8b593066c65f82ec3f82d1f0a5c9bd7750365ca5ed9755f3690201bc2051", + "options/gitignore/Packer": "sha256:a434b0b4ff90eb41a4172460fee5116231fae35b7d3fb0da290eee747d37e723", + "options/gitignore/Patch": "sha256:b2e048718be29f7461a581a2c55fa5a77acd33be1550127cc2e73274d732039d", + "options/gitignore/Perl": "sha256:3034c66a65602b0697d0ed47b6fe456685b06b6e5d3f202e37b13bb0c4a621a4", + "options/gitignore/Perl6": "sha256:0bc8fb47d904017bea2b0fcd5f41f273191264a4a0b0c9bebdd9bbcd00f91127", + "options/gitignore/Phalcon": "sha256:7ea6247b0e135cf871da999fe77ca6734e7730823b79aaf50f56a2510c607680", + "options/gitignore/Phoenix": "sha256:2e4c95ff0d46d287aea17bdcc5a4f65b8b5ca00c97eeee2b63b3c9872d7b81c1", + "options/gitignore/Pimcore": "sha256:82f1a74f2ef50a90c4b851c225e1cfc6ed2c8219fa69351c49092b3b3a12d45a", + "options/gitignore/PlayFramework": "sha256:b0e0e8fe8c07275ed9bbd19ec27c448b84b1d654efea813021502bd013792b47", + "options/gitignore/Plone": "sha256:a37dd3d1999b7633a72b066b6b0d3c4645d48e54d1acd4005ba029699ad00600", + "options/gitignore/Prestashop": "sha256:0ba03d079187777ae4337e5aa2f914fde25f2855a787fe94119b4b643066be7a", + "options/gitignore/Processing": "sha256:79519393f25d7c1f4fe4f82394f01c2f94e33af4d2d478a31c2da648d76480c5", + "options/gitignore/PuTTY": "sha256:f8d40b4427b8f0387ae5b4acae3d4126ca5ba8e0c2212e87c8ca8e8117b76411", + "options/gitignore/Puppet": "sha256:01a30bed1186684624a81bb17c4b9bf66a00d5c0d67ef98ff2b55ec47fcc5aff", + "options/gitignore/PureScript": "sha256:380c7ed58cddd93ee7c86f11aee57f52ab42c14d21620d67fd16ad3624d0df4c", + "options/gitignore/Python": "sha256:1674ac8ed7488af605be80c1f3a20bc1f4c2b47b8549346722948385ffc87e8c", + "options/gitignore/Qooxdoo": "sha256:75d7a33ff59f78c4de3dad9482b663bf4177757cbc8a36a96f0f656470200888", + "options/gitignore/Qt": "sha256:d460e722bb2b9d894385ef1fc5d730bc9952fb801a12ccd257c3ddfbb7713db7", + "options/gitignore/R": "sha256:5748fdfee6509a429dc841563e14713907524ca919767160d51403c0a41b9d83", + "options/gitignore/ROS": "sha256:96d784df3a0f3b65236da259de566eda40cdd224a526d5c957959d3a80e1bbf1", + "options/gitignore/ROS2": "sha256:8ae7ed544d5c3a7bad9d7d0759fd5eb46785f0817ea10410729f1a602bd36512", + "options/gitignore/Racket": "sha256:4e4f02d9987073165e5f417f23d5425aa9319734f8f9ec4c22e22da1b9554244", + "options/gitignore/Rails": "sha256:f2cd60aec23682be64a933624e094f91ddccc61cc12d18ea5f064cb65ba56345", + "options/gitignore/Raku": "sha256:e6382e1d20cac4ca89b76622e0802f77020a8ad807ccfd4108ca70b0e1ce8449", + "options/gitignore/ReScript": "sha256:ae7bd77c79834f638537a04942d65c96a914d12721aed1084683bb85508477b0", + "options/gitignore/Red": "sha256:94d38d49da4fc949b1190896b36811753bda1c0e1711aff8eb189f22c7f521ce", + "options/gitignore/Redcar": "sha256:cb53742b01915a5006116e50f34227788ffd4e0570f58ffe7d2154e189791f5b", + "options/gitignore/Redis": "sha256:9d7411d5272f4672c886f4210202b1f904df917d9394f18b5405d1d8b5d38c6e", + "options/gitignore/RhodesRhomobile": "sha256:2d96205d69be8079209fd1058fe1d2e7413ebb6141fd7216c8d334f2c6a0e34a", + "options/gitignore/Ruby": "sha256:f88885ba4c505a17cc766a113429b3203ee3736e2b26dbcfe26c928019bf7758", + "options/gitignore/Rust": "sha256:36ab9aeee9f9ff5ce9559260042a6566aa1779856ce66d58180ab96f71a9c83b", + "options/gitignore/SAM": "sha256:80416c71f9c7557d9bd24ff45e0f9f4089aed820b5c3cbdaf976fdfd8b09e314", + "options/gitignore/SBT": "sha256:5181693567dbe5d9b71587d6315b3a83044eaccd42f3d812083397105ff75eed", + "options/gitignore/SCons": "sha256:c81020cfdb19d358b875b5ae0c8cc4239556237f1ca23a0157b44fe2f32fe14b", + "options/gitignore/SPFx": "sha256:25e410e0c4968bd6d7882f86045b1eb09270ae68094f36246cdba61174bbc6bf", + "options/gitignore/SVN": "sha256:0c8b670851d6eb298c48bb58a70308767a153c7fd324f36c8d1acbb277aa1750", + "options/gitignore/Sass": "sha256:bc2e0a695b24d53eab69259f9a3f9bee82666663c59158d9f6a1e12c7f8e97b7", + "options/gitignore/Scala": "sha256:b66133d685c42cd9fe69dce818d5c40cb7545a6f86803919dc7ad85f749f1ebc", + "options/gitignore/Scheme": "sha256:0589eec00fee7aff151d4bc80a0916f0b5a902674f87cf886e932bb2550ece02", + "options/gitignore/Scrivener": "sha256:3708b47672057fd49b930222fc22f5157fcbbc73579a19024792670006e9a4f6", + "options/gitignore/Sdcc": "sha256:2713ab7931c0656dd85a55a1fb6c367d678a3fe222cc73428e4049036fb996b7", + "options/gitignore/SeamGen": "sha256:922cb152e4356ff94f08c78d0d388bc2bf31ec45c782123593623d28bfff862b", + "options/gitignore/SketchUp": "sha256:080e093bb9ab3a546a2255cb05f8066fe0215e70e88ad50a2f28c955feb333ca", + "options/gitignore/SlickEdit": "sha256:4975f7f29a5c32cd9a8feaf696abf7be39c29f4f33636155c57e1d94cc7238be", + "options/gitignore/Smalltalk": "sha256:b83b5513287c3f6ac77a2c98c122d1e1be94fe680709fc833caf5852a7965387", + "options/gitignore/Snap": "sha256:9da4c8e47b0286be495565e59f9edd2d95ac397814a09e7a414952df41fd6659", + "options/gitignore/Splunk": "sha256:9eb9cdce450893a290227186d13de6e8d39569f7f5bd22b7c66bcdd92820cc8d", + "options/gitignore/Stata": "sha256:b3986973727a9f3fe6e1430e07eeaa37ff601b2c2bb88f15b5a97039ddf42c95", + "options/gitignore/Stella": "sha256:d82ebc0756bf904425ed989ef0150ef4fd9446bd7807b920fdee5cb8a7969370", + "options/gitignore/Strapi": "sha256:f1ed0432fcad0400c223ed669027d7415d7dff83c57236132ed6ef0c0f3131a0", + "options/gitignore/SublimeText": "sha256:10457ff35ef28a3db4382887899d2b7d8c8b09057cdd36ffd2c6fe0c74962a5e", + "options/gitignore/SugarCRM": "sha256:b7c69444777899b5ed16481b240a89d9d6a4f1628dadd649267cc778e72807f5", + "options/gitignore/Swift": "sha256:c9981d2b6add12673e979f04c6a57abc1e1224ce4a0125ccea01f9084322ab0c", + "options/gitignore/Symfony": "sha256:b940e1ca2cb0f8b86f615b6f76d1d76d421609880d729b8bb30c471b246c229b", + "options/gitignore/SymphonyCMS": "sha256:edf283bc2c42c7a0fb82c7bc6cc1ef1b10f77900ad3f6d50a49504bc465fb4eb", + "options/gitignore/Syncthing": "sha256:0ec9f63a1028d99417d1c122fa2539cf25c5738a8a6a98351721573ec1a18038", + "options/gitignore/SynopsysVCS": "sha256:1fa30fd0bd062d9a0a98e9090944d23b2f2063dc8c1212723192cde50a14eb18", + "options/gitignore/Tags": "sha256:48e20f961490e8bc34a32780bf0bd4766a6876c3e4c76d515f7c60784f24ae38", + "options/gitignore/TeX": "sha256:a7dbb0e16fe909a4e56d1f7cfe064f03ae17b9c2b787d1771c085088395c1379", + "options/gitignore/Terraform": "sha256:9c2fab1ecf854d85ecc8f323b4d1d5e85a14421efc717e1718d912926ac63dd2", + "options/gitignore/Terragrunt": "sha256:d349052245438ef63162d747541aaa3cf88d0966d951cca8e2d8c3a1b33014e7", + "options/gitignore/TextMate": "sha256:2bfc05e14952ce99725d9230c098d938319c62421b659cde80e8cb2ce9ebac68", + "options/gitignore/Textpattern": "sha256:1cd54e1bc011f1c765541883eee2ae39bfeb7af93d479666c9974f78d0560334", + "options/gitignore/ThinkPHP": "sha256:287f3de8988db5b5bba813de6883aa076e3a022d671f375d0e20398bca989be5", + "options/gitignore/Toit": "sha256:5ef3df90e5e83141246845ca01133492752e0901ec63699f1b8cd32e4504063d", + "options/gitignore/TortoiseGit": "sha256:9b7646617692556be8acd1396c9749cb0e8d7f33710e41979a9b6bc4f27d8416", + "options/gitignore/TurboGears2": "sha256:6789d494fc1fa12a49cc91627c9bdac909221b709289003143fb7b3d9e2067b0", + "options/gitignore/TwinCAT3": "sha256:968d2cb1b2af7669d3532c8e789e281001d6b505b260f71ea62f7119d1cd9372", + "options/gitignore/Typo3": "sha256:b7aa143a155a2ff309e16ab772976bafe129ecaacaf7fe60230377dd92a27294", + "options/gitignore/UiPath": "sha256:3e64d3018e64e09bbbe88d69fc6ec03dac41c66b3c2a00c2f9e7a8fb8d5a16ef", + "options/gitignore/Umbraco": "sha256:9aba49cfc272bb354099fc2a3e6b81c944ec98aebac23b6ff153c0228e048ddb", + "options/gitignore/Unity": "sha256:c735b75517ecf18563ce4bf269eb4fe158a5539f7d554823578a85eaec795f45", + "options/gitignore/UnrealEngine": "sha256:db5bf30e0d2398cf9562305db35cbf3564c9ffb61cdbdd57c9af185c69536798", + "options/gitignore/V": "sha256:b2408aca4121a58cb96770957ce4405763d62609b6fe128edab03249d14fef52", + "options/gitignore/VVVV": "sha256:0b6d89a37e608bebc4c680c37d62c68d5e7d8c3952a7605c5cf9db7a2378f6c8", + "options/gitignore/Vagrant": "sha256:5490a90c23b6fa8a6a562c6686c8d6eca0918ac8a81db27733636cb4ba9468ea", + "options/gitignore/Vim": "sha256:bdf5190512420d9e2a917df751b89c1d68210df5e4de3599951b878a860f052e", + "options/gitignore/VirtualEnv": "sha256:eacb861f0ebdd40ddc581f3fbc7afd767b8f3371f85d924ca6dd4554ca181e1b", + "options/gitignore/Virtuoso": "sha256:4bb66fb0623e1f82b7edbf92e25727e2566715d7f111e98d1ca1731befab5d9c", + "options/gitignore/VisualStudio": "sha256:f5774e1a387a8f1b62bcfffe6759f6a06fb01f28f51f726ded190c4dfa7fd9c2", + "options/gitignore/VisualStudioCode": "sha256:6bcdaec0c325b448d6a9c815ca8aeb1ca6e701f59eb22801a91af90ac3018134", + "options/gitignore/Vue": "sha256:5ee6da3ed97910756a82856c11577982baa416ec689a41739b310578617597d8", + "options/gitignore/Waf": "sha256:7a2f032b54515d4017c45cd6d7d779dce901a65b71d176511ca6398e96a3f9b6", + "options/gitignore/WebMethods": "sha256:978d13bc2539395bb983376a9ed68a0a4bfe8e95ffa0c4bef9099b021ae7b1e1", + "options/gitignore/Windows": "sha256:d763006b3cb986d97d8d5d9081e3e82783060cb9afceaef1544c43f5e6bf796f", + "options/gitignore/WordPress": "sha256:b9f990943e1bf00367d99f4b27301d44de0b3ee46f32e2560fef27d60397c045", + "options/gitignore/Xcode": "sha256:679a2cec086814db9780515b7c74ca53903ea79412fd61b4049393920c2642eb", + "options/gitignore/Xilinx": "sha256:9e9647b1be573b2df843fb8f0bd47e534f9681f871deff8c3a47cc018adfe85e", + "options/gitignore/XilinxISE": "sha256:b391cbe75fabb940a60f27537883b1c12e5296693596235b38bf6188d2460e01", + "options/gitignore/Xojo": "sha256:6d5b434f13161268d5f3260abb6bd429b62ed7531a4b31e8238d0266dc16ada9", + "options/gitignore/Yeoman": "sha256:655fc7bbf61b76c2db6e0ca42f8ddf28c964f531b7fb36f1af31f28c68fe8cb4", + "options/gitignore/Yii": "sha256:1ddb06da611887839e4ac536dad9c3a093058496c197df9f10e96b5544cd3c3b", + "options/gitignore/ZendFramework": "sha256:07fffc980605f6a93a52cc81d37a7b4b6cfdf7bbf67676f626c59ec63c54bfac", + "options/gitignore/Zephir": "sha256:9976f9409a0f0786312e24611f90d573da669b9b31e87d1c95e906d3c904995d", + "options/gitignore/Zig": "sha256:6c5ef92843b07117a16e1ab9ee26be6efd1fc0b4e8c434cb02b15ab19bc086ca", + "options/gitignore/core": "sha256:1800dd61770fe5c93ca2e4b9a9dc1dff55b8e05a9fdc8db33e108be7739619b1", + "options/gitignore/esp-idf": "sha256:cb64c95b91db3afc3d0170e4f0434a22e197bd5048ad5d994eb0d980423b4c47", + "options/gitignore/macOS": "sha256:a7d7db989cf807d91c5610b5e31d74ece1b88da7945eebd94e1e8227b197ef64", + "options/gitignore/uVision": "sha256:7312db06c62c160e14874c248e543cb03f2ca465b4352d152d6d2958c9331fa9", + "options/label/Advanced.yaml": "sha256:2ade7da5695604aba4a3f81c3c8cd38cc1326b99b9fede751a343b57f7b4041b", + "options/label/Default": "sha256:67c04c54c648e9d21f24b2c16fa7f5270496a1a75ed55c340e8d3f39b1dc5647", + "options/license/0BSD": "sha256:e3f18c71e10d673590eb9856c1d79dd3b4b0d65404efb5e8584dbede7edd608b", + "options/license/389-exception": "sha256:44dbb5087c4a5e01f2fa8bc8888b32b9b7cd03fd4fb6310b0e064ffe0803e4bd", + "options/license/3D-Slicer-1.0": "sha256:fb6a52ec4bf7ab943ceb0e3a2c74349d41dcfab787e7531aa91062e580d69ef8", + "options/license/AAL": "sha256:0f4015683803b7c2e5252126aa72251c4bd4535363d47ee785219a7df4bd744f", + "options/license/ADSL": "sha256:499fa1d1608a106727aaa4482ff64c45091a2be9f0639f16d8509d39c33f872a", + "options/license/AFL-1.1": "sha256:1b078d9b349ed33ced8ec94147d61c1bb458a4ae88b2304f45eafb631d73bbb5", + "options/license/AFL-1.2": "sha256:a8c17d2bcdad3141ff9440224d6c3dc00f037b49a57bf7e9ebe1c315b920390f", + "options/license/AFL-2.0": "sha256:7ecc7db84298adb05d2cf10339c63a5315a1c5c270a68d97d7d40a53b508ccde", + "options/license/AFL-2.1": "sha256:93bb4f7417aa775bab9026cc3d0af28aeb64451fc5a8ec651876785edd8eaf9b", + "options/license/AFL-3.0": "sha256:de0612c5780d0d21f9982d2cadfd715d2d3b7a0dfeb532a5023d42a1d4fdce5c", + "options/license/AGPL-1.0-only": "sha256:0cf752be2bb9cdd02256cf540db397d94dc9ae2f8e74c325f62a5f8e7b3e1d86", + "options/license/AGPL-1.0-or-later": "sha256:0cf752be2bb9cdd02256cf540db397d94dc9ae2f8e74c325f62a5f8e7b3e1d86", + "options/license/AGPL-3.0-only": "sha256:d8a6cc31abc16b6748c7a21f21611f5a1ec33f67d22ca23d7da1c19b95496bee", + "options/license/AGPL-3.0-or-later": "sha256:d8a6cc31abc16b6748c7a21f21611f5a1ec33f67d22ca23d7da1c19b95496bee", + "options/license/AMD-newlib": "sha256:194934ff77c5c15488c3c3e23c9750aea0cb4a7286b3f53d2f71cdbd3247162f", + "options/license/AMDPLPA": "sha256:68b952125c3cd2641c1070f5e23d2661672d1ade688107044fb6edcd2ef8aa91", + "options/license/AML": "sha256:5a18e36d6923eaefdb77cd01adaa4c823a0f33fc50071568e4a8f7017aaf8c84", + "options/license/AML-glslang": "sha256:cc8ea571129dbee2997e52a54d1f715f8b0159a22f28f985a9b2b31708a48620", + "options/license/AMPAS": "sha256:750402327d30ead99395dd862ad388ed18853d99b66d91c5105592b99501a002", + "options/license/ANTLR-PD": "sha256:c51128da1cb801fae229ac93125141db060611c176ecadb179f21cc412e627a4", + "options/license/ANTLR-PD-fallback": "sha256:782267c0e1e51c2dcd348a454191660fa7d9ca10ead82e74f3fce0bd8083021d", + "options/license/APAFML": "sha256:708c77776f111da3d7aa6228b78d95e45fb1af359f6144b8655837abb8fc7993", + "options/license/APL-1.0": "sha256:ee583972c3b5bab45e51d3bdb254435ed7bf91ae5a7f45ad68538b3f7a4273e4", + "options/license/APSL-1.0": "sha256:54702bc17c8ac3601637577c8f92e5992be79110df72c7ff6fe20d75d4df2745", + "options/license/APSL-1.1": "sha256:81757b1a656d5e762711d6e45395fe9772db5058fc962c8f8a1b64b9d29fb9ac", + "options/license/APSL-1.2": "sha256:ea3732a5beb20493cdeb61f2d4bf78f1f1434bda046cc1ca18ac9ddda22cb28b", + "options/license/APSL-2.0": "sha256:2d0c12edaaf8d31fec6796fef99291b12e833b6bef28100975120a53d209e06d", + "options/license/ASWF-Digital-Assets-1.0": "sha256:fd7d465c176789b5734800ffc8b269f5a9cf146ada14020adebf73ed3366ff3d", + "options/license/ASWF-Digital-Assets-1.1": "sha256:556b017c2af4d7008d811daa23d25a19a8a386bb60cac19d257fb18453393e8b", + "options/license/Abstyles": "sha256:4ee039bb373cec057f2cf1e320763e08da25a506b0496a09cda5efc07970f303", + "options/license/AdaCore-doc": "sha256:ae394ea7feba5746a5036b6070abfb62cdd8d514492156bdc4ea9a441a39ed6e", + "options/license/Adobe-2006": "sha256:44890cfc9297c506de1b2f43c9050c7984798e6a0c6f3ac07ad829af40695b4a", + "options/license/Adobe-Display-PostScript": "sha256:25126d0087dfae189dbf1d1737ed4614f2ddff0755495929a08f9724c5f19201", + "options/license/Adobe-Glyph": "sha256:dd79441e1c46483562f10a93fadc7f593f337387d43d3381eb6449534e0117a2", + "options/license/Adobe-Utopia": "sha256:f0aebd95ed8fc9586cc6cbe9ddc1333a0b60b6d02fc377398d59f26fc0d1c20c", + "options/license/Afmparse": "sha256:b3030c3435ea42f8f171446210d1914e704f69d416b4c8cfc240e7a1fc4cb962", + "options/license/Aladdin": "sha256:e7a871c1033452f86767d6b6ec64e935f96992ba9f62408472556967bcf91f6a", + "options/license/Apache-1.0": "sha256:bea91cd78ce2cd6df0b21e83981a293d195b4ff53aec1cfecbd88d9de786315d", + "options/license/Apache-1.1": "sha256:1521ffd9660fbf0e4648f72e2aa164fa91eb3eacee27fe36b732b95b3d64e28c", + "options/license/Apache-2.0": "sha256:074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff", + "options/license/App-s2p": "sha256:6b2a839c39ff6485a5ac9921e942f771a3391078ae722302664cf14aeee2733b", + "options/license/Arphic-1999": "sha256:3a5e90c0957524a89e48203febcd4492ca4393678abaa7e5b4d70f3ff32b386d", + "options/license/Artistic-1.0": "sha256:b50b4f007fb64c2711e4610c8246d92f9e56b2937c9d25e1acb0416cd5dcc5a8", + "options/license/Artistic-1.0-Perl": "sha256:9e18ef4d50faba562e3efa37d8961d6e9956d1c103bfc4fc1caeb07e8effe885", + "options/license/Artistic-1.0-cl8": "sha256:a0db94ded05175a102689028b7664aa897caeb8a85cb143fc096928366b062ed", + "options/license/Artistic-2.0": "sha256:bae466ac8d7100c304fe76632ccc627203a3897d3f7166f3cce00fb9df8c2074", + "options/license/Asterisk-exception": "sha256:b35fdf6f46b0d195de5f32fd3ebd34debea5532fafed348fbf236f462fef0b32", + "options/license/Asterisk-linking-protocols-exception": "sha256:13c5d685b548ddf78a571757709401a8ac00737897f67809e9cfb118473d9162", + "options/license/Autoconf-exception-2.0": "sha256:311609164358261bc6d307fe3204b7592a23e80a75a5c90d9d3422ee218d1033", + "options/license/Autoconf-exception-3.0": "sha256:bb96c8e5d1aeba92975ad66f23f6c9316581247fd0c82c3f0d025f56edb30533", + "options/license/Autoconf-exception-generic": "sha256:f5da8a774404fd564d1d5e59196dc81987b247e7584031d1588cb244049ccdd6", + "options/license/Autoconf-exception-generic-3.0": "sha256:52d0836d9f63223dae5b6a680af6d12abc8bf15ebf5b44db8a14b7362afe993d", + "options/license/Autoconf-exception-macro": "sha256:f4d2da1bbeb699a80a1f6d06161ba6625aa766c538dddeaa5a6bb9087462841c", + "options/license/BSD-1-Clause": "sha256:244e17b6f02493de568a8ddfd7f4349996a6c332c0585d304ceaf5394becea52", + "options/license/BSD-2-Clause": "sha256:f32fb3b417a194167cfad068223fc975ba96c5960513a10f66a3c28720aec1df", + "options/license/BSD-2-Clause-Darwin": "sha256:37570d1866355ee1c785a0f12ab04f282f0433f8f22a8e9c5667829d4be4fd9d", + "options/license/BSD-2-Clause-Patent": "sha256:10500282524f6f9e8c656779f3af9975875ad886181a1f9e7ff0034bcb2f70f5", + "options/license/BSD-2-Clause-Views": "sha256:cf2d8e08c5c338804d361fee703f88f52b7fdc710eab87ccd072cf083696f6fc", + "options/license/BSD-2-Clause-first-lines": "sha256:21a068bc5c91ba13b42a0bc49ca79ddc1c0cd7de22ccefec7439d832f7b05def", + "options/license/BSD-3-Clause": "sha256:5a93d5831e1297ab10fe643e1a631e83be392896da14ee2951285a79012df69d", + "options/license/BSD-3-Clause-Attribution": "sha256:088b09c3bff4d190ca86eb8742ca494af321e3abd309c1c9ad9ccdd86dc5b3ef", + "options/license/BSD-3-Clause-Clear": "sha256:4495ed59005765e600a16457dc52a2d39757dbfeae081e68b59fe7c177c78589", + "options/license/BSD-3-Clause-HP": "sha256:3b5e77f6887af22b1f5dd3c9e5421a9fbc919824bb9440ad3dda126548560923", + "options/license/BSD-3-Clause-LBNL": "sha256:49d63fdb269af8c5209eb1dee7f6d21d7e790785a8774ca8928ddb1ca47ce2c7", + "options/license/BSD-3-Clause-Modification": "sha256:c9d6ed9e3618b2caf1e26d863a9469baf20e7ad39a6804a200b3ccd8710b335e", + "options/license/BSD-3-Clause-No-Military-License": "sha256:41e603d9270a99621857db0b3e6b2d7fecfbe4f6cd002e505fe635c2f69e2a3d", + "options/license/BSD-3-Clause-No-Nuclear-License": "sha256:54493541a36e52787ebd104f994172a4ab474ce6aff622e377457d816ca44ef3", + "options/license/BSD-3-Clause-No-Nuclear-License-2014": "sha256:a48cc8968eb873ed6c8facde24e3fe3145fd09c45cbac37ba20ab59e5e44536b", + "options/license/BSD-3-Clause-No-Nuclear-Warranty": "sha256:d3ce9bbc979d0eddb9006621ed5bd8f127af1054de064d438dd21a2c23d8bfdd", + "options/license/BSD-3-Clause-Open-MPI": "sha256:a4bd3317cea75d614ac192803a60d6f54dd15a28ea509576d4932778e2879a98", + "options/license/BSD-3-Clause-Sun": "sha256:a7bb12f504aa24ab3b3e879788303a9c9ef44e27595a81da8375405013337cde", + "options/license/BSD-3-Clause-acpica": "sha256:9c43dbb3ba77f0470afd60d079f05b85395f4834a4900aec03840c4932b08ff1", + "options/license/BSD-3-Clause-flex": "sha256:97fd685958d93be7f8dab939bb8161dbd6afb0718c63bfc337c24321aea44273", + "options/license/BSD-4-Clause": "sha256:c80d927f03db7212ba43a869a383e249c19b40859ff0d9e2e2f968e4fdcf005f", + "options/license/BSD-4-Clause-Shortened": "sha256:0006343c63f49f803c08b0cf3e918a4fbcd4bef17f3c61ad704aae3474c2472b", + "options/license/BSD-4-Clause-UC": "sha256:c82a5eb55679a3fd483d992c2428e66ee4b9b05b5b78b4ac916692368955b526", + "options/license/BSD-4.3RENO": "sha256:78a35293ed0f94c71ebde56fd8c0dfc38d5af54cf93efaf91f43a32d43660c60", + "options/license/BSD-4.3TAHOE": "sha256:e3559579df753e07c08959aefd4c7dad4de1ba036a86956a6fa18c22d5a22052", + "options/license/BSD-Advertising-Acknowledgement": "sha256:d7837e2ed1c2b86c1306d8d5b997786b37b462ac900aec56fe9e83060b9f9db0", + "options/license/BSD-Attribution-HPND-disclaimer": "sha256:50dfd9d2ba0a6ec94be6abbf2dc52a0f175ccb8d9bb50df3dde4cc63d783ce3b", + "options/license/BSD-Inferno-Nettverk": "sha256:b8c63e40314b8f741bb6ca46eb36a22125e93777089ccc38ee325cb2e8df48f4", + "options/license/BSD-Protection": "sha256:42277c58c0373c9c2ce0cd349d8e7f8da793933806b22591c63ff0bc73841a00", + "options/license/BSD-Source-Code": "sha256:1f1b677d26ab73e159a55c3a914cf12d888e18a9d0d469fedab4aa6e5a48b1c0", + "options/license/BSD-Source-beginning-file": "sha256:93a423c24f6c8ae180890239ae07fdb5d9d17bf826d8b9d177dfd12bb8a9cf79", + "options/license/BSD-Systemics": "sha256:68a2c77f4d91f98cb115a7cdb8f02b64b54072795aed28253947e632dc6da2cb", + "options/license/BSD-Systemics-W3Works": "sha256:1c8583429a62a752137d666526601bd81a47fcebc07189f55256dd0904a4e10c", + "options/license/BSL-1.0": "sha256:84c6ef3ea9e3254a54d0acf5d3e0c61ae011b8fef7dd6940591cf060e6380a8f", + "options/license/BUSL-1.1": "sha256:d90560217049db1d6020ec7cf482da4198bfa372dffd22270ba08fd8088cd93b", + "options/license/Baekmuk": "sha256:875ba3facc8d06ccd4bf853ee64e2a59a39f81e62ade4dd9387d38fa354a74f7", + "options/license/Bahyph": "sha256:d4b791741e3436c7ed335cd667f58ce06449a273cdff702039ba31b7d9f39e0c", + "options/license/Barr": "sha256:81fe1e987543218967099ed9e912d9ecbdd1148028b702665bc519e6bc70c82b", + "options/license/Beerware": "sha256:991d8a58e0b4be84a174a9bd333a8ca33807a0c1ce6d23a2e25a21f7ece482d0", + "options/license/Bison-exception-1.24": "sha256:24d39d806cc45a8c3fce2b556d8e13bc8cac59a9eb774cf8b03ebb62ba3625ec", + "options/license/Bison-exception-2.2": "sha256:002401c20a16ac592c98b2bd2342acfd20c67dbfeb1ef9aba00a9b1f9fdb1a87", + "options/license/BitTorrent-1.0": "sha256:cdc899a2347d6ca02027726202a69d3252edce26ea021b6d580235a9e87c7e58", + "options/license/BitTorrent-1.1": "sha256:69217aa11fdf55964628adbb1cfddec81d70d81343047ba00a509b34b7678742", + "options/license/Bitstream-Charter": "sha256:fca800e1a135f718196f1207c55183d7bb75ac588d7a58163a0d89a10e1a2efc", + "options/license/Bitstream-Vera": "sha256:5b3b4d3cef4029bda987a71f4bfb6087e40c5e91a2cb853fe7f6ccb78d9d8e38", + "options/license/BlueOak-1.0.0": "sha256:8a1af140fdfbf5afd3df27f7e662f989c5b963a300020dfafce42033cae9e004", + "options/license/Boehm-GC": "sha256:9d79ea74534f11db4c04bc9c0175878c0f79641d335ff23d5b5b4d7671924509", + "options/license/Bootloader-exception": "sha256:e95f6b21fad95ea10ef0fdaebcba9b8ea777fce7a757fbb1aac1325be9ee4117", + "options/license/Borceux": "sha256:aa0f864b15e20c58b552928cc65e4266d8630f47f4136be00c98f9ceda7b6eb6", + "options/license/Brian-Gladman-2-Clause": "sha256:2f78867bc743630a86137c136bc61356b2186787587ed16880762e86227edb8d", + "options/license/Brian-Gladman-3-Clause": "sha256:c7823399907fa696b8634dedf908b80c0bed592a297ced3bbb4034a07cd06470", + "options/license/C-UDA-1.0": "sha256:010e48b7389abc09621b16c332d3e7d138ce0815bff07e67a5cb05ef5407058f", + "options/license/CAL-1.0": "sha256:a24987ff96bf2e331cb7d90989fba6d7f90a37e07ac1b99927025c3fb4a5df36", + "options/license/CAL-1.0-Combined-Work-Exception": "sha256:a24987ff96bf2e331cb7d90989fba6d7f90a37e07ac1b99927025c3fb4a5df36", + "options/license/CATOSL-1.1": "sha256:c30f1f9fafed19c55115432c9dcaaee2cccf26dc810e514685f73992c3cc1db5", + "options/license/CC-BY-1.0": "sha256:1e4d54d3d9badc97d8bb32861cb3a449903129d02ab4482e142c85afe132b8a3", + "options/license/CC-BY-2.0": "sha256:f892996eb4b6f2e54241432ed67b743772224e191ba3a0ae595eb4feca3df4c5", + "options/license/CC-BY-2.5": "sha256:82d2eddedf451177a12c9753e518352f7f90fd4ca3542111c499be981e9a9600", + "options/license/CC-BY-2.5-AU": "sha256:0e035b0af59631c22efc3cd4e914b8eff3901ecfdb955a665a5f418aa71f9cd0", + "options/license/CC-BY-3.0": "sha256:e6bc9e9c474700b708f568bac9e5a8a9bcb2b1dad53442f5ba449fcb848b8e76", + "options/license/CC-BY-3.0-AT": "sha256:445ceca0d524644d683235522f139781b1d7810f7a90b09de1357bc2117025ea", + "options/license/CC-BY-3.0-AU": "sha256:5c36a065df50fc007eafef30c05950ae33146f9538da9e5bd4c6520eafc65cc5", + "options/license/CC-BY-3.0-DE": "sha256:5e5ee84a40c935e2709273060c30cba7b8fb1413ca3bcc34ae72812bde119e00", + "options/license/CC-BY-3.0-IGO": "sha256:26d9706f1a4fffe751365b249c23f8bcb0bb2aff2c76f57608bc6bffe002001d", + "options/license/CC-BY-3.0-NL": "sha256:aacbc3e84cc7000ff659c8564f29ab070a02662758c78e9ea601919e1d25a6c2", + "options/license/CC-BY-3.0-US": "sha256:df36d89de58066f49fa9b81f4290ea409adbbdd9d69c620359efbdc8bccaac3c", + "options/license/CC-BY-4.0": "sha256:d557539df68e771cc1eedcc91d13f70fca930e508d11eedcafa4b15db49e3744", + "options/license/CC-BY-NC-1.0": "sha256:a73fdf076f226d20892f9057491359b010302605534591cd6c5aba5b0a380d99", + "options/license/CC-BY-NC-2.0": "sha256:6093e945ebf2222650e6ac25f0ca304dbd01e3bb72be0699bc1dfdbccb81736c", + "options/license/CC-BY-NC-2.5": "sha256:4af6cb2a17379821538198ada4fb742e827bde59a684f3b43abc91966105e19b", + "options/license/CC-BY-NC-3.0": "sha256:45526cb7a15234d8d686a3ba5f11b607516f90662d125d5423eb2b57f474e5e4", + "options/license/CC-BY-NC-3.0-DE": "sha256:bdc3d2bf54dfe572f0f8c6134e0ed3bbfceed89623777fa2b61b7af4ddb0164e", + "options/license/CC-BY-NC-4.0": "sha256:cc97a2c4ff7723a1f25179dc7c18f676fa84191bbb36ea7d0061acaf3c102559", + "options/license/CC-BY-NC-ND-1.0": "sha256:0ee05d1926693585746403dac464122d3c63e1a6ec71c0b27add13a351c8e13c", + "options/license/CC-BY-NC-ND-2.0": "sha256:a2f17dc1ff568703161054535575c4ee813e6112f4706958efd446525c007197", + "options/license/CC-BY-NC-ND-2.5": "sha256:bb90b847abca05d60344e952af821e5e9bae1bf7d2e7797a3e5e4c0d7bfe2db9", + "options/license/CC-BY-NC-ND-3.0": "sha256:d74e67958cad1ae4cb585402ef66ae41958ba546e6c5e80a93920013cb81bc2a", + "options/license/CC-BY-NC-ND-3.0-DE": "sha256:4278b50eadabfdf513c8f4b671e366d7bba295b30bdcaaa2c489fde93239ea8b", + "options/license/CC-BY-NC-ND-3.0-IGO": "sha256:3519844fc261aaffd6e93beb29a0e37373d8c6116968264b1e67d310fe79cdfe", + "options/license/CC-BY-NC-ND-4.0": "sha256:4f3c4a321eb0c73182eee7afa3fe0111e24dec193f2d6febcf4207f26af26839", + "options/license/CC-BY-NC-SA-1.0": "sha256:0cdfdfd86e7fb5f1e524141fa032f8c35208be199b3931c53c61fbbd4d502645", + "options/license/CC-BY-NC-SA-2.0": "sha256:2a8a63bc21a0623eca903515d3e080e5dee1d39f6645c95ae79c1d6d91251606", + "options/license/CC-BY-NC-SA-2.0-DE": "sha256:b9c8e57ed5574559f0045c116bc4b8e45a31541b1135635564a79462e1d2412d", + "options/license/CC-BY-NC-SA-2.0-FR": "sha256:0167ea1c1e7cbe892b94ce3188ee5ddda602a6c602ca173876220fa852bf5ccc", + "options/license/CC-BY-NC-SA-2.0-UK": "sha256:42ef58839f2a106619788b869467bba338e95ebc77a56fca4ad9c11ac619e161", + "options/license/CC-BY-NC-SA-2.5": "sha256:a938300f5187fd01bb6077d110e340d7093447cb120648ae5c59566d153182ed", + "options/license/CC-BY-NC-SA-3.0": "sha256:c6648565ca4a7b22b2cd06e739dfec031313d2f5ce5f31c5c994f6aec9f3fe5e", + "options/license/CC-BY-NC-SA-3.0-DE": "sha256:dcb55267ce6c09173bb8c87a274df8e49cb19a2c575d9ba2cfd06aa1eccaed43", + "options/license/CC-BY-NC-SA-3.0-IGO": "sha256:493d06d0942a099c3510bbf03b1c7968d24e7c87f4c9eab2e3913d943c4e4c92", + "options/license/CC-BY-NC-SA-4.0": "sha256:600ca4e25fe11762b75a97e714707fab48bb778374e92d24c6ca068791661c11", + "options/license/CC-BY-ND-1.0": "sha256:2bddbb1d9047a5f8152a7662d5410ce9ba6d3ae0f3a702aa2e68f616791f3905", + "options/license/CC-BY-ND-2.0": "sha256:c71e2baa7c4d65216f0e4adbbcb5e1e94f6796e551e2eb73b4f18522b165e7ba", + "options/license/CC-BY-ND-2.5": "sha256:dbe30e90636898129f91d925c43b95345c2a45529b7630d1eeab04da2eba9e7d", + "options/license/CC-BY-ND-3.0": "sha256:b9d9816260e23b4bdb71e0edc453497273f4cfc1fdf61207ee6cc1c7075a2479", + "options/license/CC-BY-ND-3.0-DE": "sha256:7be24bdc46d0b9ef85616fb6aa7b9d334ebe0a44ccbce7742c3ad75e7ec8fd20", + "options/license/CC-BY-ND-4.0": "sha256:4a9d465e981ae6bde7f117abbfb0e24f2cc1820e99a47efbe58593fa5c43fa29", + "options/license/CC-BY-SA-1.0": "sha256:826ab33293d21891d7119d6d3bc658c0ac25a756df77bb733be910503e753154", + "options/license/CC-BY-SA-2.0": "sha256:6d6b3bb586e5f4ae7e6a955556442fbfcc2a6cedb89be5e4e0492b2983e40760", + "options/license/CC-BY-SA-2.0-UK": "sha256:794bc0365e8b31fa70d4041ec7166b398a587c5ca95209dd1ac1d69f7a8c1fb9", + "options/license/CC-BY-SA-2.1-JP": "sha256:3149d336715b0a15e7dbc6454efd13a363ceab2e60c15264cb48252da0b03736", + "options/license/CC-BY-SA-2.5": "sha256:dbe7fadf81c75e0cb15a0cb338a6f42a7b34dced43958a4921ca056ca1b87a93", + "options/license/CC-BY-SA-3.0": "sha256:3f941b3b89cf7b8370ceb83cc76d2120d471b58735d8ca60238a751a48d7f72f", + "options/license/CC-BY-SA-3.0-AT": "sha256:81184828fbd0059bbb5216306a3b03dd268e16c3bfb2470ddb269e1deda323a8", + "options/license/CC-BY-SA-3.0-DE": "sha256:dfac6ca9c9b3082919f8e417e522e561e11c8402eab83e1099650b8e4347412d", + "options/license/CC-BY-SA-3.0-IGO": "sha256:f0409ed4e4d66769650b863f1150e7ce9b61be5d4330f97b3975110e733d11ed", + "options/license/CC-BY-SA-4.0": "sha256:cde7883b9050a1104f4ac19a1572aafd6e5d7323b68351aaf51fbf4beba54966", + "options/license/CC-PDDC": "sha256:40f05ccacf018e7311690cd03bba659a1329b3df9775d2aafd33715d52541fe0", + "options/license/CC0-1.0": "sha256:a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499", + "options/license/CDDL-1.0": "sha256:fde9ef48461569987d09cac803379245dfda04e7751075f39b578a0ce927d470", + "options/license/CDDL-1.1": "sha256:b5bbb5ac38b5cb76862a7f410ca567e4b32d7c22f22622ebff10ba434827bb78", + "options/license/CDL-1.0": "sha256:8ca6d06e4e0e8ee6397ec94aaebd6fb3d6537237a7b1fa1efc676bef957e0325", + "options/license/CDLA-Permissive-1.0": "sha256:52aa39b7c72a3712b1e1ab0b2d6b9d790f443f985407672b9df8edab9eb64712", + "options/license/CDLA-Permissive-2.0": "sha256:4531a67d443284d93ffed0803df5b10634aff21c3d77e381f2d48af01d875868", + "options/license/CDLA-Sharing-1.0": "sha256:6419d7a7245cb8a9da6e3dd925d05d824ea0268e3527095f68dec091f84f75da", + "options/license/CECILL-1.0": "sha256:39f0f3429a386e4ea95a3316187005a82678a640a79903e26da99fdf58a5c623", + "options/license/CECILL-1.1": "sha256:f6e7067cb4f0ce195cc66767bad5f527aec66be5e29142c9fad984efb7aadc73", + "options/license/CECILL-2.0": "sha256:20ef5a0029fd269919dca199876a7e435eb24e59e0ddc942fe8ddeb4078a0868", + "options/license/CECILL-2.1": "sha256:4ea234937bc7b0aa5247e436690d1eb9324875bc7590ecde50befd38e35190a5", + "options/license/CECILL-B": "sha256:ae13622d13fd432d2e31ed4cb205667d98e203b0cb54b1f244bd8d8354cefd48", + "options/license/CECILL-C": "sha256:7b00493e6580710aad04faf2747d74b6de840a47183f76f5a3645f1fce69245d", + "options/license/CERN-OHL-1.1": "sha256:6d1ef4fa73531fc09cb7e8dbe6d0f11dee648d3d219182f33bfd8eec9dc15f8f", + "options/license/CERN-OHL-1.2": "sha256:e5d8c837f87ce76c91e5595e71e55e6f25595fb406617563dfe15453ec5e97c3", + "options/license/CERN-OHL-P-2.0": "sha256:eeecc593866fa1c3b80189c7e3dc0ceb77740557fa92f81771f891cc54c579cb", + "options/license/CERN-OHL-S-2.0": "sha256:d6475a84171f0cf7ff52667dea714df268fc65cea42f0bf328b0b70d4cd0ebab", + "options/license/CERN-OHL-W-2.0": "sha256:c1432111d0aeefc9d1ced8105d2d4ed07d5dcb1fd43abb5d4616c4f9ef96e08f", + "options/license/CFITSIO": "sha256:805453d73d82fa5bedec1ab5afdeaaecd3797e6ad735f5b58324ce0363a941a0", + "options/license/CLISP-exception-2.0": "sha256:a2f03cff1cb319b76676a8262225e7ba4f65c905d257d0f3913c79df606c54a7", + "options/license/CMU-Mach": "sha256:22eeb34e3e2a837f21ad0de0fcb13af7a4fa4c2d61a999c34d4a8ad62d3ca98d", + "options/license/CMU-Mach-nodoc": "sha256:4f3142a42aec8694327191d7edbdec2896ba62f076ed6876232076563f1c16c2", + "options/license/CNRI-Jython": "sha256:a2acd6ddc03c0afe980da1ebebe5beb8b120026518f096d6a9c9d007d17ee5f7", + "options/license/CNRI-Python": "sha256:69b5d7d41a6ece3b20c6a77774f567948766d8643f7646e3f65474692cce5c15", + "options/license/CNRI-Python-GPL-Compatible": "sha256:8e1f50d9b3c33a6a58559a27042475a6267c7ebc745be85f2eaa2ffb04878dc8", + "options/license/COIL-1.0": "sha256:468662ce418b08a970e4a6e175402504d399b0f230dd266be42f7b7b12629941", + "options/license/CPAL-1.0": "sha256:54cef60295bdffd5d41904338632538db1f3cdcd4c5f7ea5bb021d04eef14d92", + "options/license/CPL-1.0": "sha256:9698f2ba346a875a47a9cc6bb602e3d126758774dde1f575b4f3369dc3f2574f", + "options/license/CPOL-1.02": "sha256:d69c13067c9fdf52f2b4d9e26478c30821abd7d96bd31968eae78c829f9b1e64", + "options/license/CUA-OPL-1.0": "sha256:2fa6914d4062431b1e2616ce0ed265238d8eaaa58ded803860b888f12b978aae", + "options/license/Caldera": "sha256:fc237785d2ebe63d4ff8e52b3ece122afdadacbc187b5d092fc37042d6065c61", + "options/license/Caldera-no-preamble": "sha256:f5a5d863ea1be445f88b95229461bf19175e1f11a7cf7f61d227a13fa319aa29", + "options/license/Catharon": "sha256:942d5d330259d528b7b0a8cbf8ddf919c7f07d726fa6d4ab8a80999050e3f43a", + "options/license/ClArtistic": "sha256:70fbadfe7d99a766987cba5748021824ce261ea6fa6147a1ae6a451f100cb90e", + "options/license/Classpath-exception-2.0": "sha256:f36ecd7d28dc3c49854d3908a3824696972aed34f5ffe426174f35fb03c5f194", + "options/license/Clips": "sha256:12ba45745237cd6fd4c0c4d0462d9dc875a18db24ababa74f3980ce2cb574d9c", + "options/license/Community-Spec-1.0": "sha256:3291598ecec867f166a20e7538e173b05fbe0af9d739a6cabfb27121d93fb96e", + "options/license/Condor-1.1": "sha256:4ec040dad1b8a812433489685590273260bd8039906379aae7a235cb585bd74e", + "options/license/Cornell-Lossless-JPEG": "sha256:d72d91523e8030b56b033e84824aabc68aa68c2d08329a6718a2ab97bb6e073a", + "options/license/Cronyx": "sha256:475b84942bba1dbb67bc6d28bc897ad5f9961840d93e4a8f785b3e788462cb1e", + "options/license/Crossword": "sha256:2d68d48c27390b69ce67c613f8ea8a485410eceda7125a834ff6df094a62bed3", + "options/license/CrystalStacker": "sha256:2fab343deeb18d20ff0e23388a88a6c1505077c27a4ef49cbd189dbc90d5279e", + "options/license/Cube": "sha256:eefb4a0ba74f94ab9fbf57c4391cddc914cc86248c788c0581497c93088788c9", + "options/license/D-FSL-1.0": "sha256:d3b9533606523db32fc7d91c47ec04187452d2b6fbda6dbe9fbd50e89652641f", + "options/license/DEC-3-Clause": "sha256:f42d3afd6860db6ea2547c8db561e98c2c87c57d193cfbf80ce8d8e38f3fed83", + "options/license/DL-DE-BY-2.0": "sha256:6c0be9ac9d97f3ef693a1758bb301daa1717ee8ee7854110c079162e9edcbe2a", + "options/license/DL-DE-ZERO-2.0": "sha256:d8b7a2dacf795f36d8cbbf0a12a8d2b59d8c2049db50c700598f21f99d785b9a", + "options/license/DOC": "sha256:de40eb797e5b9225c42d93b157a0d6f0ea5367f950adb1ed731652284207a67d", + "options/license/DRL-1.0": "sha256:8c3b3b2f84a0d9d4d0a24f358c4515a84676bdc802d2965327d385745b321de3", + "options/license/DRL-1.1": "sha256:db8ed64d721c42044109255da4ca1a351e1be2631e0be5af118fd70c6fa28246", + "options/license/DSDP": "sha256:796f94844f13d4cce27e4d71e1a2782e7b6a69f31d1c88956e4d24a556533998", + "options/license/DigiRule-FOSS-exception": "sha256:3cb5633b9277ceea6e714f2d20d5ac47615ce1b65317b6557ecd18e057af9904", + "options/license/DocBook-Stylesheet": "sha256:13e08f237e71dfd7f100cb0a82e8cff320fa8d07d6cf1876c0b17e9ddad7475a", + "options/license/Dotseqn": "sha256:173bc5170b7f83a138f8b0cbc30530cbb09273e6ef608d072cdd180e26cb1f1d", + "options/license/ECL-1.0": "sha256:c3b96326d1e6bb2e8826af2d25eefb181b3deb5293f2deba3229e3c5ed26df42", + "options/license/ECL-2.0": "sha256:49831d277a9eac4e7a31922554e24fb61142e7a6534da2d15fb062ce0dca77bf", + "options/license/EFL-1.0": "sha256:76631db9123a74928a98da1b833f3b4923f718c9528bbc40e2cd1a77a6d2cc4b", + "options/license/EFL-2.0": "sha256:1d7818c8d04401c9e6b6c4b18d084fae31cfce24e022b368c9061adecde819c7", + "options/license/EPICS": "sha256:89235e921d859ba6130b584a6ec0ced3151167623207fc03ce95513adad009c9", + "options/license/EPL-1.0": "sha256:f3d8dc494be2e7ea9cd31ac2fb902e35dce7ff8181b9b30d05861b47c339cb25", + "options/license/EPL-2.0": "sha256:61616a4066701bc7888ad910de1633dbc55eb4d4d30d930feef55afb9492e33c", + "options/license/EUDatagrid": "sha256:6508739a64a180fd64de79d04b3b297bca7a9f10d34e18da5b2502101c3a0dad", + "options/license/EUPL-1.0": "sha256:88111434fafd94ba8d30e8dae9dc222dc57328af4d8f8910868fc9736d82cd72", + "options/license/EUPL-1.1": "sha256:1480d19681cfc637b0e83b9b52fe9fc91bdf571d83e7d4ee141c2860b1b15b37", + "options/license/EUPL-1.2": "sha256:57fb42fbcd0b037ce528ed8f72f1ec095d67bc6825ecf1448ff39be1fe68a4b4", + "options/license/Elastic-2.0": "sha256:48255018b41fc0e965b1115af7e6779bc218bb8a6747d561da800d5022622aa2", + "options/license/Entessa": "sha256:548c65ff7aad4d42b4f1bb714f98df03cc686fb1d5b3d7a0e5225691fd848f5b", + "options/license/ErlPL-1.1": "sha256:d8b221d8996f491ba9ba8ebb12f89cb362a5208e8973e31e5baf11a33884c501", + "options/license/Eurosym": "sha256:d637da895576888c3b9c31e416bfe4039b3273eda95f6295e046a26aef1c9cf8", + "options/license/FBM": "sha256:2119028b24dbe85666e9e4c1891499314c688452652a047dd1c93309e83f8e4f", + "options/license/FDK-AAC": "sha256:c90edceefc6bd9352cc44912a94fcf74a683d918a3995deadefbf10a6770f6c2", + "options/license/FLTK-exception": "sha256:f4602375d7542419e93d99c308239b03e32001cc4e98f494f0c8ca0758e12de5", + "options/license/FSFAP": "sha256:99cb91dacd5714a701749ec47239015726beec5fc86a5c94a4873778824b16d7", + "options/license/FSFAP-no-warranty-disclaimer": "sha256:3a40f45db703245444eb47b1ff185380c57f0c34fc1a815f9ac6be2860250147", + "options/license/FSFUL": "sha256:a5e99f55b10212abd954f092b65ef5d4cf4047071fe20be307406a148a342c35", + "options/license/FSFULLR": "sha256:6d425c2caac889c969c8c2ebb18b1e2326f0d8c7f82e43c0e6c9c957fe3c6cd0", + "options/license/FSFULLRWD": "sha256:e1d905d45b956dcf22c5a7bebd382f3c8c90256ee46148dd479fc821097522d2", + "options/license/FTL": "sha256:ced6622122ce451cb1ea0c3c3f507a640e2a44c075c04900ddd9fae8acb5369f", + "options/license/Fair": "sha256:59954017f5a14cb3589a7123d56f96905ccb3d6ad58019432fdd0d91b3c2b541", + "options/license/Fawkes-Runtime-exception": "sha256:e1ce38432762eb43172bc2b9297b6ee5c07eea1585140edd180bff6a92b84828", + "options/license/Ferguson-Twofish": "sha256:c518a7865c8aaf8f04c7d4c7b75de3ae0c734f68472e73eabcb787929e0dee60", + "options/license/Font-exception-2.0": "sha256:000c7fc2d306b8f74db81d1b44ce9a96da4c1674185714cbe4e9d6b82db45aa7", + "options/license/Frameworx-1.0": "sha256:073806e49e909804a7f97a843215f658cefe0a5de3cf15390a51f817b1dc37e5", + "options/license/FreeBSD-DOC": "sha256:5f8b56ac9004a40244eba78f9db2661c7e0c22b864dce6d6a6fd036ecf1a4c6f", + "options/license/FreeImage": "sha256:eb3a4cb8220bb3cfcb1fde4b07f39481afd93bc7c9eabafdcf06292611ca9b70", + "options/license/Furuseth": "sha256:66514ca238436e5566b8caa40426fe5f939e00f306a30d4a0ea4f1ea92087d65", + "options/license/GCC-exception-2.0": "sha256:2657934bffd8c59b27a38f31c4bfc4cde9137a8e0cb168e1a272bc754ab78f0e", + "options/license/GCC-exception-2.0-note": "sha256:71acf2b6078c4d36950ac55f4ba7d4b5506b5c8746c51feff90d5682d5871495", + "options/license/GCC-exception-3.1": "sha256:7103d4f7f7e2f8ce10d282a05e0689637f8d6d9ef7b399d808d1da313e69b960", + "options/license/GCR-docs": "sha256:c0142ddc7c67186f98990f8e7558747bdfbfcc76fc865335177bbc8d785ee860", + "options/license/GD": "sha256:5294cfc75f9d13209ae8c88ecda14e255d4ce8b27f282e924d33f7e7845ed8d7", + "options/license/GFDL-1.1-invariants-only": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.1-invariants-or-later": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.1-no-invariants-only": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.1-no-invariants-or-later": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.1-only": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.1-or-later": "sha256:25fa19cb49d292047f3f16879301ae3a496b263a938f74e1840906d97f6d0211", + "options/license/GFDL-1.2-invariants-only": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.2-invariants-or-later": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.2-no-invariants-only": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.2-no-invariants-or-later": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.2-only": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.2-or-later": "sha256:1118a91a3290bc8981f922dfcffc1a619592c007aa31b7a98cee694d933c86ce", + "options/license/GFDL-1.3-invariants-only": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GFDL-1.3-invariants-or-later": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GFDL-1.3-no-invariants-only": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GFDL-1.3-no-invariants-or-later": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GFDL-1.3-only": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GFDL-1.3-or-later": "sha256:d024962f1f4966c102b40d9fd9cc834a3f0b3c1397b41444b7cd7925640dc929", + "options/license/GL2PS": "sha256:58c616ccc0034975b82c0ae8d0dab9e7704ee1be5e670be7b81a762cc98c2346", + "options/license/GLWTPL": "sha256:761baf8222655fcaed12b56fc70fa60ef822ad233663dd2f8088fba5bf07906a", + "options/license/GNAT-exception": "sha256:65877ed77981399921c194a0a899c0355abae64ac6d938d26ab3fb0992d3113d", + "options/license/GNOME-examples-exception": "sha256:c9296cd40417ae31090ebbb68257ec1c1ba3ec0a51754e6d281a5630453961e4", + "options/license/GNU-compiler-exception": "sha256:8cc0807167ca1a95e384c9a9a9b44f941a3a4a84496e1c1db5263dabac5a5a6e", + "options/license/GPL-1.0-only": "sha256:da37ffd5fcdf0a7d76af862a4330ea1076e1443b51782b2f912148357b7acabe", + "options/license/GPL-1.0-or-later": "sha256:da37ffd5fcdf0a7d76af862a4330ea1076e1443b51782b2f912148357b7acabe", + "options/license/GPL-2.0-only": "sha256:aaf135472f81c5b4a0dca9367e5bb5e9750032b5bebe5442b36e4c0a47430df3", + "options/license/GPL-2.0-or-later": "sha256:aaf135472f81c5b4a0dca9367e5bb5e9750032b5bebe5442b36e4c0a47430df3", + "options/license/GPL-3.0-389-ds-base-exception": "sha256:2e4111d48b06612690505eb3fe6c63b13d7e86df578c629c8ce7b581b14b479d", + "options/license/GPL-3.0-interface-exception": "sha256:723a4d4ee0cc163203c7d5730e04a1216e69e0748860909d35149be4fbe3610c", + "options/license/GPL-3.0-linking-exception": "sha256:08b90705856be7ecea7ee7ac46e3c45f57a0981e6f735595b978f6735dcbad14", + "options/license/GPL-3.0-linking-source-exception": "sha256:aa189fb0e461c79f4062363209dc3cb01f3bc1c367e62fbc1fa6be6b04e9535e", + "options/license/GPL-3.0-only": "sha256:fb981668c18a279e285fc4d83fba1e836cc84dd4daa73c9697d3cfd2d8aca6e0", + "options/license/GPL-3.0-or-later": "sha256:fb981668c18a279e285fc4d83fba1e836cc84dd4daa73c9697d3cfd2d8aca6e0", + "options/license/GPL-CC-1.0": "sha256:340ae872d625b128586b8751f3d599f63893417d8867cdb488702c48ea58fbc1", + "options/license/GStreamer-exception-2005": "sha256:d2796d7a11b9c864307b793e66b45db29d43febfa763065f90f9b92525fb0e43", + "options/license/GStreamer-exception-2008": "sha256:f6b64fbfdc2007b0114e33bd1e912589d9ed5e88ade53a6ec0b63c5bcb4aaf44", + "options/license/Giftware": "sha256:ea1f4b4145e906a6a6a0fb2288dd6362f175f58c69a24b598092fc53d74fc96e", + "options/license/Glide": "sha256:5eaa9ab5acc3e11f248e77eeb1c77d699064e1c3cad78a9e7db1bc4867a08ad6", + "options/license/Glulxe": "sha256:38df8b60705e5b622c66f431a11e5cfba9350e66ed0ae7eb529112699bfdc142", + "options/license/Gmsh-exception": "sha256:2ea30b3ed0edc602fbbab8291a56ffda20a8d28be7e0c80973e628b1c10a8250", + "options/license/Graphics-Gems": "sha256:026ad6cb0b03c812839238ceaa759349166ccad7ad67ff3fea58387d6625e5ae", + "options/license/Gutmann": "sha256:b1a2922b815f46c73ce067c65792cf557a65e2be00b309c303e2d9bb01244b17", + "options/license/HP-1986": "sha256:9aa6d2493f6a371ce19d3538f352bbd80ee81190c4403cda7f1d9a6d878cb41c", + "options/license/HP-1989": "sha256:9449a02544080235468891055a21335d4f038801a680e510d79989eedcd224ae", + "options/license/HPND": "sha256:4d9ae4a36816338dfa153d1d6f92645c45f25eaa46908d3096922e2431fb571e", + "options/license/HPND-DEC": "sha256:140feb81600f4af1eb0010e02efd7ef2ad04f16b13a5fe7d4291891b48a5d218", + "options/license/HPND-Fenneberg-Livingston": "sha256:9fb0130e0f701fdd89f46ca85a5560bc0f605508e102e8d227ec28cf27b0675c", + "options/license/HPND-INRIA-IMAG": "sha256:46a10f27acf359b66f98b0a07ff781495d0906380d2bab7c15f38ea658c1938c", + "options/license/HPND-Intel": "sha256:9ef7a1ef2c62df2512378aa071bf5d2a5f7842b8230585401ceb3e1d99628701", + "options/license/HPND-Kevlin-Henney": "sha256:9c73641270f020fce60e4b6aa93f56754763916517cd72d21a73527696ccd3ed", + "options/license/HPND-MIT-disclaimer": "sha256:dfdc68c52916f8a870e82453f2c90fa9415fe82ced891f2f68e2e4f2d3d52ee0", + "options/license/HPND-Markus-Kuhn": "sha256:d81e0150606531af1ea188093a1e62846a51c83eaeebaaa1cc7e94a3415deced", + "options/license/HPND-Pbmplus": "sha256:a361c810da87802193e85503747f5b4ecfee6344a3c554de99896fc915e354b3", + "options/license/HPND-UC": "sha256:f75f12c110026b77dc75ba2d92d3b84acbcfa7ce705af1ee7bd0589521724ad6", + "options/license/HPND-UC-export-US": "sha256:54f8e7db87bb74ccd25d62a32d2c613b9131fd8e21b6d9ce499d2d67ddbdf196", + "options/license/HPND-doc": "sha256:ad07be322beb4cd68f4e8f0d4038ec560f3d54c514b5710106a3846e894e51af", + "options/license/HPND-doc-sell": "sha256:b4ec6564fc15f466e4b3908098db5aa810fc2587ef4ee32e1739c9d7ca7c21ab", + "options/license/HPND-export-US": "sha256:45263c2b3d0b5a81d3876bc59d5d75ea85b90b843a18d9377a9e725a35493e8b", + "options/license/HPND-export-US-acknowledgement": "sha256:09dd3f5048d0b39a7f11af7fdaa963a048ea2aebdd69acdc25b20848f3222c2f", + "options/license/HPND-export-US-modify": "sha256:9f8636b1c04b96616d14bc2e609606742dd5ed4479fca628f465b94bfb997da7", + "options/license/HPND-export2-US": "sha256:bd33905481ee786386948a8a223c8bd162aa6753d585379215bf31e73298c4db", + "options/license/HPND-merchantability-variant": "sha256:6fbed4fe5e3ce1a51aa3fe597f16dd9619c36ae4f1e4cf850dd97cf1fc0bb5d9", + "options/license/HPND-sell-MIT-disclaimer-xserver": "sha256:3407f7f65c7205db74d8c62b1c93fb2ee420981e399c395a4c53cd55118af292", + "options/license/HPND-sell-regexpr": "sha256:c853f16b4bb23d0310e049270c6440f79caad98c52bd43f8842091a9e233fd62", + "options/license/HPND-sell-variant": "sha256:235abc578371bf9861e3d6eee0a9ad16228f9be18408e17fc569c6a98ba69d5f", + "options/license/HPND-sell-variant-MIT-disclaimer": "sha256:5a1da266b8931ca44bd51326cf08e709b0d95252013ead9dd06d3acf5533462a", + "options/license/HPND-sell-variant-MIT-disclaimer-rev": "sha256:309460909fbcd7d372aabae10c79dd7fc4d96bc47fde26adcc19ceff5fcaf44d", + "options/license/HTMLTIDY": "sha256:2cffd83fc5d377d1390acd010caec134710aaf88baf06d40c4c82692a7d9d94f", + "options/license/HaskellReport": "sha256:5d28692ce2562cd7e0eb0ed6f087f84e71663b14d319e0ebb2abc8f84f1e308f", + "options/license/Hippocratic-2.1": "sha256:e64f3c8d11dcff7bcbe739ae7b2d3ef755cd3dadf47273ad3cdebbda8a40f58a", + "options/license/IBM-pibs": "sha256:833925bbe8b90270ebcc89ac5cff0e2f1f298e8a174f54d1e11e7d467d6bd21f", + "options/license/ICU": "sha256:c7922dccc709c9e19ab6ba970fe00af174229484b05160fb07a3913f4d04cf50", + "options/license/IEC-Code-Components-EULA": "sha256:f72284157fbc0b39b7b65491abc3012f62a81839e2d28609032df8d3d9340814", + "options/license/IJG": "sha256:7658542977bfdced9e1059a6c934ce4281de76d103b831007b825917dc62511a", + "options/license/IJG-short": "sha256:fb29aceaac2fbe300b6d0791b007efb5a54844c971752ffb771779b49106eeae", + "options/license/IPA": "sha256:f5449912f9a791699330354ef8b83347edb849a74a66d2577324b76c2acd6c15", + "options/license/IPL-1.0": "sha256:0d7fbdd478bb103ab440618af177275cbc47d936b1703214091a360dd36116e0", + "options/license/ISC": "sha256:f2ec607f67bb0dd3053b49835b02110d5cd0f8eb6da3aac4dc0b142a6b299be9", + "options/license/ISC-Veillard": "sha256:f9449d12f6aa6cf4a86ec893bd0ed3b397e57ad7f2d78aab2d680ceb38068c3f", + "options/license/ImageMagick": "sha256:0559a7a7cf47eaf00332b60f8db520866cc09899eaceda5fb528a24c184626a6", + "options/license/Imlib2": "sha256:19d21c4402df7020d631511274d991393267fd56b59aa014c0be90becd7631e4", + "options/license/Info-ZIP": "sha256:fa0c6949eee13c4099a6d0f78f9f1d76c7320e71adfc066d1632837320703a08", + "options/license/Inner-Net-2.0": "sha256:1beeb7b196011ecb20843a6de48dd34346341a3c6c97923ac2cb5846f4320d27", + "options/license/Intel": "sha256:6337a14cec9c05002528eb0540048a09a64f22850fec6c415962e262deaf81f3", + "options/license/Intel-ACPI": "sha256:438661edc91b5627914befe431a03535a2bcf175614cfdb26c7aa6bfbbb0cc03", + "options/license/Interbase-1.0": "sha256:f3a34d96829d93613b291b98cc01baeb65ae4b85b6d1ae35bc6b86ea1b037112", + "options/license/JPL-image": "sha256:3a0c8eb612b77118be08dbff72000b058d14809157674d047e4b06e4626a77ba", + "options/license/JPNIC": "sha256:656bf1cb2fd290432c8728df087a71b5daf363c6fc4863992590f2567fd58930", + "options/license/JSON": "sha256:8f175cfd3fb4528377836d75a7e28829c997a12b356dbf5cd1c6c85b1f11e49f", + "options/license/Jam": "sha256:5239625d8bf548b9c3cc0934b7f60ca781780db7165596f8b28e38e2701dddc6", + "options/license/JasPer-2.0": "sha256:c9911ffd3e0d18770b1fcdba9fb7cc1ecf942cf924771440e35872c40ae9e3e4", + "options/license/Kastrup": "sha256:9e1cd57616f0ecd134872c3792532be05b9e2654d19703fa2ce55d731ca423a0", + "options/license/Kazlib": "sha256:31e4a00336d15d148a53bc173852f06b8b2063126fd09966106de11351fbadc2", + "options/license/KiCad-libraries-exception": "sha256:5cfeedcae118baccc28df6e77ac978b6383fbc668418755f4bf274d2d531ad70", + "options/license/Knuth-CTAN": "sha256:3b61d86ae3fd94caa0febd5b24f68cf37f8dd915e142083bb966c4ca037634ce", + "options/license/LAL-1.2": "sha256:66451dc4b36e6dfcd1a1cd6db69106fd5b11e877331e4af8094519999983101a", + "options/license/LAL-1.3": "sha256:b6ca3a455ec3f5d116b5c069d26f5e6dec8b164e1af8a06ba31c4f65306219f5", + "options/license/LGPL-2.0-only": "sha256:86dc99d7e5060915ab1dfc1378b7dd351c62088bfa74067e8aa1868c6fdba7d8", + "options/license/LGPL-2.0-or-later": "sha256:86dc99d7e5060915ab1dfc1378b7dd351c62088bfa74067e8aa1868c6fdba7d8", + "options/license/LGPL-2.1-only": "sha256:5749785c8bdefafcb5d798270ed0a967036fe2ca63dcedade1627565dfef81d2", + "options/license/LGPL-2.1-or-later": "sha256:5749785c8bdefafcb5d798270ed0a967036fe2ca63dcedade1627565dfef81d2", + "options/license/LGPL-3.0-linking-exception": "sha256:ba8baef109f934bf1832b45a75a22f2737bcccdac1211745c2bdcda9499e8a37", + "options/license/LGPL-3.0-only": "sha256:996af0513df21f7496288951c41428a03c174e9e4a9d63665c57d670f845ccb1", + "options/license/LGPL-3.0-or-later": "sha256:996af0513df21f7496288951c41428a03c174e9e4a9d63665c57d670f845ccb1", + "options/license/LGPLLR": "sha256:089a0a0b01dd68e9f804355f76772a010c03d6c543a0a9664e24ef5255cc0b62", + "options/license/LLGPL": "sha256:d0487c31bc8ce4b45b977a237e110f90dbd841397865b51779d972f890920c93", + "options/license/LLVM-exception": "sha256:e34c58338bd89d43e709e226610d8f32b3e3c47f4ad9a99a8dc1d4ac7842488e", + "options/license/LOOP": "sha256:da529c5c3f538f467762f7963488bbb37f42417226056e1d312af0e18e66bf43", + "options/license/LPD-document": "sha256:1eccbfa25c87f3e888f750f93a44d2c2a0974b161f3849ddd01ba67d783d2a1f", + "options/license/LPL-1.0": "sha256:84110c251f5fda9b4f91eb38164891b6cee6b1f9a64efdcc71b4f8c062150296", + "options/license/LPL-1.02": "sha256:bc4fd6a85caed81081c052ccdd86b03912bc29fb6b9f4117e6e1de438046a57f", + "options/license/LPPL-1.0": "sha256:7790736e17a15a9e30f8adaacb3951c7b3a9e102c2cf48b9ac9a51bdf26297a0", + "options/license/LPPL-1.1": "sha256:9ab49d3841a6076004a7964a045b9aad65eb0fe7dcd862b318dbf6b60ff67e95", + "options/license/LPPL-1.2": "sha256:f6781f2b7eabd350d9c070a592439ca516b68c3ff6f9ab9b5a9e0afa38d75712", + "options/license/LPPL-1.3a": "sha256:d1d977d5a7f0dd55e800a1827f6eb0241da6e0c32cbb7d66b8feae98834bf003", + "options/license/LPPL-1.3c": "sha256:f0f307427080e67974f58f6b7f732039a966b3a7ae480dedee3275907104ac1f", + "options/license/LZMA-SDK-9.11-to-9.20": "sha256:9e90d0107591c03207b35c9850a748593e3ef93d22b787cb7ab78bb35f978c53", + "options/license/LZMA-SDK-9.22": "sha256:6e31b1f0c4ae78e10710525955b2356adec878c918e86d8ee796b2925223ab88", + "options/license/LZMA-exception": "sha256:f9b8e3025d7f43cc32b20182b1353eb932cd76d83813f99173538204e7a952fc", + "options/license/Latex2e": "sha256:5d68e4a9f72d6c1845be14efba0883926d093ab1a8e3fce765b0bcd7c98b77ef", + "options/license/Latex2e-translated-notice": "sha256:2c5a5181fd771b2957a89b82e314604c42cad79ff71c8db02088710b803a6903", + "options/license/Leptonica": "sha256:74fbc283706af770c44448ec1c1fe27d44e03eabf4f6b6cb9ff62c5facdcc70d", + "options/license/LiLiQ-P-1.1": "sha256:e29a35c0a27e3072a69900bcf8c478de3dcf31acd7058ef23b249b0bf5501ddf", + "options/license/LiLiQ-R-1.1": "sha256:efc4ee7d7064ec8c6b3103db8f525222aa0105de173c1b1c8a488e19ffec4439", + "options/license/LiLiQ-Rplus-1.1": "sha256:a26dc35b0586cfd36cf8f854c731ea20552d916a1d9f43387aa6636ec371838f", + "options/license/Libpng": "sha256:7667a8c88c7a63690244988d626bcddd27ed895526e2c3ab1a9adb463a5fa287", + "options/license/Libtool-exception": "sha256:5c1b07009d9d2e8543ed64ece7433e75d9bb35adab57b9663dbc0a68f8dbe3c8", + "options/license/Linux-OpenIB": "sha256:6503d0418051ea29997c4c4d950386fcc356c9459a73a62d5d67707d04739b92", + "options/license/Linux-man-pages-1-para": "sha256:f0c23664f8d8f73ed33aaba09b4686d05deb2723fe1cfd8fbc1f697013686cb3", + "options/license/Linux-man-pages-copyleft": "sha256:e7241c6405f8ad70943bcdb0fd83cb79fb25eec601ecf311acc9c72f19794d5a", + "options/license/Linux-man-pages-copyleft-2-para": "sha256:9404a643cfa5e118b50ac14584f5ed01d63f2e14d7249068f3b69bdda73846ec", + "options/license/Linux-man-pages-copyleft-var": "sha256:5eaba1f816b73ae7c6742d167d8ce64ef5ffdacde8597dee824ea8570b881ec5", + "options/license/Linux-syscall-note": "sha256:5bcef4fedbfc08776630e65d5d0d074dd31208037eddb167ca46a9ec4f737764", + "options/license/Lucida-Bitmap-Fonts": "sha256:a08a969727e153e76ed2837e65a88d3deece93794cc9590db503f17c58b84cdf", + "options/license/MIT": "sha256:b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0", + "options/license/MIT-0": "sha256:59746d6285ffa44bfc7ecada352aa5d6a20dc8eab418a60ce091cc739012c135", + "options/license/MIT-CMU": "sha256:9454a04f14e2a889b3f08bd9973f39889378cb5bbb64008533c36ea685cda936", + "options/license/MIT-Click": "sha256:bc2e9c8b6ccbdff1223ef1fe8866814934707ebfe230a3cdd7016e40a6335ca2", + "options/license/MIT-Festival": "sha256:8ae0bc761d5f10ff26a9b13d26e8f81c2eaed5081cf0c62582df3a5aad15fa2f", + "options/license/MIT-Khronos-old": "sha256:fbeaca472f4f70e276dd1106ca5097435967a22ad6c1d8200aef7ad9f70aaf3f", + "options/license/MIT-Modern-Variant": "sha256:d7366190045ad81a5e612799b8f4eef242f9b389a0040836aad61bebe9849647", + "options/license/MIT-Wu": "sha256:c0cb8b3ce560f5e3dc4d55e65e3da650e76ae45863066fa5f5d5a1439db5e153", + "options/license/MIT-advertising": "sha256:9c81f1a97e353c75c4eb5ea689a88ef72b55eea49ed370729872b96af0ffb1ed", + "options/license/MIT-enna": "sha256:f9a92117b604caf0373ca5dfd120a3ce0aae882faba6c6b70b0991df6492227e", + "options/license/MIT-feh": "sha256:ed74c3623b7953132928e64837175d85d14d28ca1cfe03c35b57c49a2caba448", + "options/license/MIT-open-group": "sha256:8e06c2e84fe9275766d1c22a5f194cd264f9664c568c3bdf1356624a65a4cfc4", + "options/license/MIT-testregex": "sha256:6f24234c5af1086db34e47ad721d10335aa3f32f65e11aa94637399a387e5adb", + "options/license/MITNFA": "sha256:d436716f7a05466f52443893079761c8a88f4967d0ef490fabc0a1c9ef1c3a1d", + "options/license/MMIXware": "sha256:40c3f31b74932093092d5754835c7a05c69f901aa9c295d3b819343d9c4bc176", + "options/license/MPEG-SSG": "sha256:071c67a3669230dbf0c7f4bd8be5bb95659307bb1ae82f846dcc0896113e8103", + "options/license/MPL-1.0": "sha256:819b323d7e6d8685b4c0fe9c297c75d59f2e70eb52fcc7e14bc2e65d70b8f916", + "options/license/MPL-1.1": "sha256:6214f8b1300bb9f37b16ebc146f8f61af0187e0025042ff95c8b3030744a8795", + "options/license/MPL-2.0": "sha256:66a3107d5ad6a058aab753eaac2047ccb2ed0e39465dd0fe5844da3e300d5172", + "options/license/MPL-2.0-no-copyleft-exception": "sha256:66a3107d5ad6a058aab753eaac2047ccb2ed0e39465dd0fe5844da3e300d5172", + "options/license/MS-LPL": "sha256:2e7e676908e39585fb60e431c629ed01e3c1f52bb8a4b17b83099db68d0253da", + "options/license/MS-PL": "sha256:7a162b1da10f1c22db4c68f07bec4a8355259f8d4aab5b00a2b2bbd423d833dd", + "options/license/MS-RL": "sha256:7c69e20ff8633d1b5135126aa9921a2156f6a52b355e92812a084211afdd5d38", + "options/license/MTLL": "sha256:b3999fd83c5efc7758af8c5ea4e220f2f17938e5c3b69730591dade823e8666a", + "options/license/Mackerras-3-Clause": "sha256:36b3b92cdf9674e405d1be8088706aa7b2a948b0fa237a780f1a7a643d1e0688", + "options/license/Mackerras-3-Clause-acknowledgment": "sha256:f2c3240abd5792c68c93e54310ca1dd6389313a91092e78039d2619948b6beac", + "options/license/MakeIndex": "sha256:d0e7ed2b5cb9423dfd990e6559416babaf82a08b238f97c3298b2b9475af0531", + "options/license/Martin-Birgmeier": "sha256:6162a9f6c4a186b562bf090cd3f57c5f1f681480eecbde75beeaffaca5bd7040", + "options/license/McPhee-slideshow": "sha256:5cec066327c1b70d8b9d730f8568563edd9cf189c537466a448d95b42c8c655d", + "options/license/Minpack": "sha256:c87b7f8ee88f6195e91743820c00354833583aef091b72e2d4a49c8e28e798a0", + "options/license/MirOS": "sha256:79da9e5c37adc25a27cfa41f1f64cc93cf2d6be8e52cba3fbaf02b9e8fafd3d9", + "options/license/Motosoto": "sha256:37346f4fd2ad2c7978247ad7457541da73df065951bd73e58fded807e7c38c45", + "options/license/MulanPSL-1.0": "sha256:b43343cd1c032cd260893c7ed384157418ef7468bcae64547f4fd8e49f93572e", + "options/license/MulanPSL-2.0": "sha256:eb7a1d713eb919b146787629e22e4c975cb701f529a65d4d7e0fcd417558bf1c", + "options/license/Multics": "sha256:d61ab7b7b6e9d7871ef6e5217e6b7cb915dd6549cfa68a49669382090b81fe70", + "options/license/Mup": "sha256:2150061850ba3f03176b10c680386fd793923a3c28394048e8c1ca611cee3240", + "options/license/NAIST-2003": "sha256:0cc49a2743d845a9f3ea44ec3d7a6acc0aa8f0380772020310f3389713c45cbc", + "options/license/NASA-1.3": "sha256:fe59e577293a15d0e8e732068b759272a11beb70868b7c8baaf4351442b64893", + "options/license/NBPL-1.0": "sha256:856b837d14c15e0433c9516008a5a39e6807455ddd2d01d056bfb05ebb5c540f", + "options/license/NCBI-PD": "sha256:53f6e0d0c9c596683193234d749741b43e39be59611d2e3681a3d150218d1436", + "options/license/NCGL-UK-2.0": "sha256:8b47bd0a080565f15820162902a5068cc5902ad46cf90bf30a4f59b8c363a7ee", + "options/license/NCL": "sha256:004758fbde3480a5d5ba92c391449d6380749d55404a508ba217ef32b23a6492", + "options/license/NCSA": "sha256:b31bbfd06e221b39e1a877fb3f91b32fe9e88c87c1b46d11cd2bd5c822ec71e3", + "options/license/NGPL": "sha256:c0c36141bbc888c6a2e724e2cc2b2529fccebc93fdddd7b960336c525e201082", + "options/license/NICTA-1.0": "sha256:a55d054f273794f1f2f2d2abbdb6c1dded02878df1d5cf1c81a81649e1b41381", + "options/license/NIST-PD": "sha256:df5608400c450115c45a01868b3ac1909c967ca589db779148adcb328d34fa35", + "options/license/NIST-PD-fallback": "sha256:fbd75808217bf1b9be0c209976435557e094322c4233211c4497f485fa987906", + "options/license/NIST-Software": "sha256:ebdf2cd74b947cae9a26ef198a7a84f2e20a6d4feb252bd569de89c38c3e7ece", + "options/license/NLOD-1.0": "sha256:d5573513a243e2a7b8464c63281907e01245659f405d3efccc8b00cd7a59809e", + "options/license/NLOD-2.0": "sha256:b1b06f8f7771ed8d4ee958d984ffe269f48ce18ff6719e4a8ca59e3c6ae84361", + "options/license/NLPL": "sha256:bd220cd4ce2e9d09093135fdf4d3eacc4d44c9866426c56c96eba86dcc2d925d", + "options/license/NOSL": "sha256:c85abab0d02c1df3940601b5ccafa1e01a8aa9bfafbdaa9bfcf77d717f617e9c", + "options/license/NPL-1.0": "sha256:900bc8e09b27d4b32acb3eef312817ef528c49323a080dc78b12526261fe41fe", + "options/license/NPL-1.1": "sha256:728c887df6e36163a6fc21d2eb3f0ce38d7bb14fa0608a65e06f867575d13a19", + "options/license/NPOSL-3.0": "sha256:72bd990f4e37c938dbb54b3063cc8a4500e7ca27410a9988976314e02858f75e", + "options/license/NRL": "sha256:f22576bb7aa46c8f188ef1289e2bb13dd8585a8ff53851b8fde834e7b43fc839", + "options/license/NTP": "sha256:290eb479deee5ab45484cb98f7cd6c118603208a4f4a118e34f591b50fa20934", + "options/license/NTP-0": "sha256:95f2d2be04f88370e06c806324a96b1b1f57d5fc50f5da9ada19acf4b70206ba", + "options/license/Naumen": "sha256:ab999583e09a53632496f4614a2f587f4ed19cecb57a558aed92a0d1a4f3da79", + "options/license/Net-SNMP": "sha256:edf2a6282bd3574dfe90461e1ba69325f27331ca072c5887f6a42b9e89495908", + "options/license/NetCDF": "sha256:1e78978ebd3b198c0eff362d9dc63cbdd5ffa6654484dcce44cc17510c47be17", + "options/license/Newsletr": "sha256:89c9cbe8677d8bd9a748747a6b412a1b03cc242eca241641d9a0f7969b4201c2", + "options/license/Nokia": "sha256:15425a7c7bbde70efc9adf491faeac81d45d275d083c50c383557ca0b687c453", + "options/license/Nokia-Qt-exception-1.1": "sha256:de1a377dd135af411b3d5d6c77aee10c8150ece3b7e911bf2cf139347a6a21be", + "options/license/Noweb": "sha256:1718bbd9a0a6b2e03fbb1f3e6e11cb0bfbd95e29cd5eef5988eb42a1c26385f9", + "options/license/O-UDA-1.0": "sha256:7bf99bb9cff7b91883ff56e1eabbf673dd5d9ce163316735d42a645a5cb1f1b2", + "options/license/OAR": "sha256:07cffb7517187e810518a3dcb1c4aac6c99f061ba87888539cb20ae5821b21e4", + "options/license/OCCT-PL": "sha256:f27bc08863387270b4b7814aa2aab8616f8ff6c42ecf604c44d96c4b186a0c81", + "options/license/OCCT-exception-1.0": "sha256:6814878c4f9e653cca06d533c034ebc14b050c479ac4e90694a96ca5f3ed619e", + "options/license/OCLC-2.0": "sha256:dbf0de8b59347abf26a602a8cd8b32f1162207801fb3e3eb24f33afa16ffb6b8", + "options/license/OCaml-LGPL-linking-exception": "sha256:442eedab496337d413cd2537a564cdc63b2033b25b56a18ca1aa83fb054f99b4", + "options/license/ODC-By-1.0": "sha256:b1c4de4bf91c95eec945ea7cbe0575fd52e7ee8fa430acb01d323d8214b1c482", + "options/license/ODbL-1.0": "sha256:77d2692c3d64efdd4db18dce2407699baf62930e2b50d6e5ed90b48acf16b7c1", + "options/license/OFFIS": "sha256:752f1eef8948e5bbaa33079f0a031a407c3a7e42df4ba5d41c967d3794370b65", + "options/license/OFL-1.0": "sha256:7e6a2968a3782383e1b427aafaf7f7c7737fff024294c4a4d0347557b1aecb10", + "options/license/OFL-1.0-RFN": "sha256:7e6a2968a3782383e1b427aafaf7f7c7737fff024294c4a4d0347557b1aecb10", + "options/license/OFL-1.0-no-RFN": "sha256:7e6a2968a3782383e1b427aafaf7f7c7737fff024294c4a4d0347557b1aecb10", + "options/license/OFL-1.1": "sha256:8eea8287e5876b539670cadb82e99f9a7afddec6f6730811be1daf25d2e9bcfd", + "options/license/OFL-1.1-RFN": "sha256:8eea8287e5876b539670cadb82e99f9a7afddec6f6730811be1daf25d2e9bcfd", + "options/license/OFL-1.1-no-RFN": "sha256:8eea8287e5876b539670cadb82e99f9a7afddec6f6730811be1daf25d2e9bcfd", + "options/license/OGC-1.0": "sha256:95deb073011c1c2ae37c66965fe59f0405ef87b4009a36d9418cf42ab355b4fb", + "options/license/OGDL-Taiwan-1.0": "sha256:6fb1f786e1d278b6240b4816ba791d48fc84223e35da8a9263e42836bc154e94", + "options/license/OGL-Canada-2.0": "sha256:05c8b379c2fc4c491ac5899cd937a8c21176feba572c52811de0de7329b4cb7c", + "options/license/OGL-UK-1.0": "sha256:e3371a2e1b91cd17a191e13bd28e63ff7ed158090b13157cf78b7e44dd8cb389", + "options/license/OGL-UK-2.0": "sha256:dea956be58d1944c806229baeb5f4c3ade45decc2c855a1d738ae0ad014ff408", + "options/license/OGL-UK-3.0": "sha256:eb22384c866b63b513254ae3baff6b3bc7d001f559d03e02b6b8b83333a04806", + "options/license/OGTSL": "sha256:1866b77e78b70d1ef5dfd7768fac63ea25e31837c4520be4eed553c9af792908", + "options/license/OLDAP-1.1": "sha256:48c9b242e69207b65c04b2c66ba32eabaa0b919c81368df0eaa39c615caf812e", + "options/license/OLDAP-1.2": "sha256:db22cb1867b8e1b1f9d81b3aeefcd2abb4e2e7234b1e5fb7009b1100eadf3f4a", + "options/license/OLDAP-1.3": "sha256:de86d2e938d43c21d83e06743fbfd3aa62e2327503b5e4132bac084197aca929", + "options/license/OLDAP-1.4": "sha256:5d6fe52457ed4d79d28e99ebb11063e5fb7be137ed001e327831873ee5c34724", + "options/license/OLDAP-2.0": "sha256:9aae161f485cd3c2c1e5eec46081914114d01e1b10cd8976c916cffa26b431ff", + "options/license/OLDAP-2.0.1": "sha256:6b3601cc98f64d1f2ef1b38812089bd0f0414377be6ec6e6a54c239115f75f36", + "options/license/OLDAP-2.1": "sha256:87425cd0c08b6793e8e80eb1cdb834c36e920bc39ca2e4fabe17735ecce844bb", + "options/license/OLDAP-2.2": "sha256:9f1fae576e39c76d698474446cab0e6f540e89cc9a4823381ad657a0f7cc1d29", + "options/license/OLDAP-2.2.1": "sha256:eba72913bfa6332516aa78309c3fe19482007b448a3b3a5822d2452ce686409b", + "options/license/OLDAP-2.2.2": "sha256:e57710b30df448549502dce8418e5fc181f2c6a511eb3e8239adf6c2e4f3f17d", + "options/license/OLDAP-2.3": "sha256:9ee0c6441186b94dd2eb0d531e86fc0db7941ee8d75acd43b73493569f31adbb", + "options/license/OLDAP-2.4": "sha256:4b548d1398e2c267e968e16749a3a89875a870a2ad072006c6dc4a346a6351cc", + "options/license/OLDAP-2.5": "sha256:7016e17ba63ff052e08716cb4faa54e984eabacbebd6f73299796def8b76a809", + "options/license/OLDAP-2.6": "sha256:9fd3f54cdc045d44fa951b9cd927516e09d7688bd3b375e50a7e3681d77e7112", + "options/license/OLDAP-2.7": "sha256:cb371808595ff77a5d9c57770ca799bd4d44e5aa397790126cb176308a13c590", + "options/license/OLDAP-2.8": "sha256:bf30f8254b6698a79e867d0a7d641bbbd3460f7c2f16cab9c6816718815c2ded", + "options/license/OLFL-1.3": "sha256:a9996155436c8ce5fd46b3f3f5403d6d604c8994acbd9024946d505bc000a4b2", + "options/license/OML": "sha256:6d6cbb65c30870b70e1e261fca3c0d475acc1b26746824bee0db446be5452b76", + "options/license/OPL-1.0": "sha256:426dfae606856f71a18cdff4d7880fdfe72b5ace21b6af9fdd9c3102d6ea1c07", + "options/license/OPL-UK-3.0": "sha256:bff83b51ab7d4a231d3d1aae23a08fd83418c6a93c35599624789c5871c5d1ab", + "options/license/OPUBL-1.0": "sha256:82e4552fc0641224de50b710b8cfe78a6e75c9b31d72e494203e10f67e58bf61", + "options/license/OSET-PL-2.1": "sha256:ed99f44eca387bd812c680bba47e765779a8d721e9edf483e42fe272a206b3f6", + "options/license/OSL-1.0": "sha256:cce19c78a02ab21b89016dcefcb0fd6ce82a52593ce2ca19d83b82a8ea347884", + "options/license/OSL-1.1": "sha256:2cb78904f44dfd665afc4753432c328421632f5ce9389d70a42efd3905ebc4d7", + "options/license/OSL-2.0": "sha256:e1f0417f3b2df980f3c61a26f9417c00ebf325f1a5ec4704c5edfaa70e50a13d", + "options/license/OSL-2.1": "sha256:0545a54adffe26d0cd2db10a5be3b26dc3bb7b3681d879a60a698927f7b2bd03", + "options/license/OSL-3.0": "sha256:9329b26bc40104b49da35be76b7e972dada9f2561ca19d17aed4801bf5e1623c", + "options/license/OpenJDK-assembly-exception-1.0": "sha256:2d5460cc56455aa2f9f2709dcc335df029de0819c0fb9b29d1e5e91a43063618", + "options/license/OpenPBS-2.3": "sha256:260b415e93abd31eed15f4d520b4e86c6b0e9317c0541d6004242d499078ae59", + "options/license/OpenSSL": "sha256:57bf9f4cefe46ae1ef5b3ae83505f01ec8b2ac94a473e2c9e3fb1f80de1e23b8", + "options/license/OpenSSL-standalone": "sha256:58732ea797186959add662812bccd02e704e0ecd65b1ec6b26b72e449134337e", + "options/license/OpenVision": "sha256:a71e90df5b123331e63b50a498665f032d73c44415c216a79eaf13e15216d390", + "options/license/PADL": "sha256:385147523f99b7b5b41d1dfb093fc3c466fb65357b8022521b088468cac839e4", + "options/license/PCRE2-exception": "sha256:9bb8552f23d2a44e265c13e83e16928853f29675cc37d3887f71287e9040c4fd", + "options/license/PDDL-1.0": "sha256:561ca6d9bba2a5b8658466833b15035f64d067fc327d226250f5123aa96e82af", + "options/license/PHP-3.0": "sha256:8c18adf2c052e52aa8376d40a445a840af2edab91aa5892a78bddf9421cf0a9d", + "options/license/PHP-3.01": "sha256:c288e228ae7ccd27a39a7d4011057c85769a85e5fa38e91e31cec1f5549b1e7e", + "options/license/PPL": "sha256:e3a912d134e5cfbbaa53e8a583ea920eba8a78e05869e1298d9f9369c200663a", + "options/license/PS-or-PDF-font-exception-20170817": "sha256:a50c22c8599d84e2d684a0c8ced4b024a5ef24e6e489fcff8a2e771fb49c75e2", + "options/license/PSF-2.0": "sha256:ab745c5061d1dea43a3885e5b4b6befc7e983954954775c5736debeefcdfd89b", + "options/license/Parity-6.0.0": "sha256:881b28495c80e972b4b8cb3fd3235bc5341781db6944878172f0bf79c5c53ab3", + "options/license/Parity-7.0.0": "sha256:5b3bf2359dfb9f95483270da7833be8fcc9e1758abffabb045991ec7c51ef796", + "options/license/Pixar": "sha256:c52718780b46e8c7ea48b9ae0406061d4918761e385b6e024571a37c6d02dd95", + "options/license/Plexus": "sha256:22b94623b17585bdde3e1fd4f1749da139b656ebf5cf63c26d5c5fa9680a1aa8", + "options/license/PolyForm-Noncommercial-1.0.0": "sha256:ffcca38841adb694b6f380647e15f17c446a4d1656fed51a1e2041d064c94cc8", + "options/license/PolyForm-Small-Business-1.0.0": "sha256:2f81e317d36f83a199c9fa10bcb250775fd90510303eaeb15aac2d8f0c1fc2d6", + "options/license/PostgreSQL": "sha256:d443a7dc550da2be365b4e924bb8779101e1c318d15bb78e5cb6374cadd8b6c0", + "options/license/Python-2.0": "sha256:893c2bafbb8133f7aa97e1f79a3ee3241ebca7025f56278e9e1f72bb98592f9d", + "options/license/Python-2.0.1": "sha256:1d165c0d255094285fe6ce754b431b9efc1e7df547db4aed5c3b3d082e5d5aaa", + "options/license/QPL-1.0": "sha256:524dce795dfcfda14f8ee06e738f3059c4a054306d07dc14601164ed94cd01ce", + "options/license/QPL-1.0-INRIA-2004": "sha256:36ed72077a7854285b3073c5c104afd36fa5d0077ab77baa85be56ff72c8660c", + "options/license/QPL-1.0-INRIA-2004-exception": "sha256:083e76ab5c9be947fba231b640a4984f294b7a65df4fffae2845ef86e256908f", + "options/license/Qhull": "sha256:e8437fa1b9bed4bdb334dd598aab3a3c319f28664d935b9cc2ca690ce8c8c444", + "options/license/Qt-GPL-exception-1.0": "sha256:fad799fdc248a52b9c0fa17823b6d4bc9907a19d6e8c78b8a071c9b551ea9c60", + "options/license/Qt-LGPL-exception-1.1": "sha256:79949ee74297883df6e4e7c4bfe1e75a6e7695376b4a87ffc013e27bda509cb2", + "options/license/Qwt-exception-1.0": "sha256:e32ab09c810c3db30fd650396dc6b4cd022d38aabd31f61e732ea87141f6d16d", + "options/license/RHeCos-1.1": "sha256:4ea0e1d136be8623d1d7f93df765f4021d5a11e17169b2356c6462dbd3d3002b", + "options/license/RPL-1.1": "sha256:ea8d9811a051e7de6b7404ea1795dc4233a95d92950cb8c213e0f4245f181cac", + "options/license/RPL-1.5": "sha256:9bdb5f2d585451eb429d69f48ae0b7c6bd3954eb6431b3d644a7cbec074e9ecc", + "options/license/RPSL-1.0": "sha256:358012b3f023bec75c2c90ebe3cf2ea2330e2edc5404e7f810237ca66d146e20", + "options/license/RRDtool-FLOSS-exception-2.0": "sha256:ce7572f466b3778fc7e228565af0418750a3b4f399739ecc863543389eb27c65", + "options/license/RSA-MD": "sha256:91a375b6dee034a2269e061179262e1080354aa22f420fbf22a6aeb265cd060f", + "options/license/RSCPL": "sha256:190ee7c947dc7f20be4345d5fa9ea09e6fceaa5df0b02e87c6689cfbc3b6ee13", + "options/license/Rdisc": "sha256:a389cabf55506a4983df23b75ed34c45affef185e5b670927b914189948035b9", + "options/license/Ruby": "sha256:dac64527dc40e0be96d3316a93c0b5adb64ae47c7dc546fbc9fae4f0268cf6b6", + "options/license/SANE-exception": "sha256:69d1780a6eb75837b22567afcb00786c013b074d5d93269579ad4715f80c7111", + "options/license/SAX-PD": "sha256:e451dbda697567122de73277794aee9555d4ab47621c8d4fb4ae970816597edb", + "options/license/SAX-PD-2.0": "sha256:c5bcd7d2c463d824e35028b197129fe165aca926d0434202c7a64b3028c3810d", + "options/license/SCEA": "sha256:0698b27d130fb6f3195e4c50361441398e88e8378a753a13840e1160f5a7e397", + "options/license/SGI-B-1.0": "sha256:fb7bdeebcec463ec8da4756f750b6544e382e0b011462325f852c705b87ee4ae", + "options/license/SGI-B-1.1": "sha256:bc095a0fd1a4b55d6edbe7325fc9c3194092da9712f4bfdabfa11d5b8c65ea32", + "options/license/SGI-B-2.0": "sha256:cbe043cc819b6afbac13c11d83c8bdcd566d1539881f17cab28615edd7af3acf", + "options/license/SGI-OpenGL": "sha256:68759c1c36ecd64ac0f00532357550305e2d9225e47eb11f2b6b021df7d60cd3", + "options/license/SGP4": "sha256:ca904c7522c495d88cc6b7dd7341efcefa41e9a71cc141b3695403c12302c9bf", + "options/license/SHL-0.5": "sha256:e6e27c5b0e01fadc487f39caa237935b7365891bde56b6962188846f1eec6ad2", + "options/license/SHL-0.51": "sha256:3a8703a66bc3ac9a0c001549499b2528b01bc0e5a12c6fd968f7551e5b320448", + "options/license/SHL-2.0": "sha256:0d4bb5faca4b9bb1b22a56369bc7f3380a8ee5a2d228f0ebcd2fe95d8b10a62f", + "options/license/SHL-2.1": "sha256:6fa7fc99563ae8b86d910c33820be9af08052b7c527bd61d5661b0c2cbd623e9", + "options/license/SISSL": "sha256:51a1c03478872c36943737b04cc35a6f903d4f3a6bd9144c90af5831f44c0e75", + "options/license/SISSL-1.2": "sha256:fc21555cebeb4ce3f92ca353c149bbeab7a6abb7bc049cbbdc82a5fdcf8e4c60", + "options/license/SL": "sha256:4f856f272b788c83d8787cfb758c2def06b7f9b5a2fb000f380ede8d854310e9", + "options/license/SMLNJ": "sha256:772a7c6e457671d358559c6f242da9dc79f63b9301c93e22314cd5c032a1991e", + "options/license/SMPPL": "sha256:33681dc201fc6a1c028a57fea1830b81a2bc77207df925bb0d87603892343d17", + "options/license/SNIA": "sha256:91d5b2dd186cc5d201f35feba2106703457c4746b3f2cc677587c83887b0de2d", + "options/license/SPL-1.0": "sha256:5a38bd15c6ddcfa3937a4434126f899e597537a68da2fbbd4b219652a673734d", + "options/license/SSH-OpenSSH": "sha256:93ef72accf595b5da226a3492881e88b8464450ff13addb3ce4a3f7fdeb9f23c", + "options/license/SSH-short": "sha256:1aa57d2704f6783460f2750a9f3217d083ed5161eb95438ca43a5674213a7128", + "options/license/SSLeay-standalone": "sha256:54b10939070f848802ba699ce682c5cc2fd66c4263c8abd6ba040c4209d0e2a4", + "options/license/SSPL-1.0": "sha256:3fac2f3a7404f72330ae38e3a1d2632ede9ad3fbdb0d471d04c33f2c1d0e94ca", + "options/license/SWI-exception": "sha256:9da23ea902529428ab2e16eb98599766f331d7eb9bdacfcc355d29519140ec97", + "options/license/SWL": "sha256:012aaf808a2dc69ac992b0902f20246db69a6b330fbc9f2d31f49473809ea1fc", + "options/license/Saxpath": "sha256:a1e209261a4b23b8d8a43208e948e24ea81be4f5a6b4ad726028a0885c3eaaf7", + "options/license/SchemeReport": "sha256:625cb81833e80ddfd408112546f001a21ad85a23879ce30427e76cd40da25cf2", + "options/license/Sendmail": "sha256:3e9d88d6efc1150456f660c2b375be7e9af5d7147bf90bb6c79f4b25a1e21424", + "options/license/Sendmail-8.23": "sha256:3d0eac09b2b9a24df644d55d074435461b7cc2dc4035acffa992e6b0f9a583ce", + "options/license/SimPL-2.0": "sha256:c914635c3f2fcb078fd728f76fc1b0dc5abd4e976061bc32f2cea622b4a1b5ec", + "options/license/Sleepycat": "sha256:74e179cd51dffda479cf3a339cf53c4dc46289fd72307f246af6c45de7b9cb5c", + "options/license/Soundex": "sha256:03b2035244ff4f72de122c057065a4a88309f87ed0fbf4aa84b8e8543f6d1416", + "options/license/Spencer-86": "sha256:49e59df7e8775baf32d2b9b77d3413e5481a73b38a9b65898597be22aa662242", + "options/license/Spencer-94": "sha256:7fc18e76f0c124a3a568657b99e33df9bda7e4f71a493dd63d69956eb2117660", + "options/license/Spencer-99": "sha256:301dd94995194d81c49471a77e02957df588a2ec847dd2571cf797a5ca9ab65d", + "options/license/SugarCRM-1.1.3": "sha256:0238949ba5e4d3662c6718a601a05ed7adc9bda6b1ab6f2d566420c388abb1f6", + "options/license/Sun-PPP": "sha256:059d4483c8805533d6d0835631c9055e06ee1162af496d2014b3e8829289c0a4", + "options/license/Sun-PPP-2000": "sha256:f6a7697354c7d77cb22f779070d5d0b08a7b9b138a5343152380964d3bfde8c8", + "options/license/SunPro": "sha256:f5ab1c2763182c84a0b991a82d57b4176df0d109ca5d485d8446585696b26894", + "options/license/Swift-exception": "sha256:3fd829e25ba4b34da7bfcb6dfb201e7bda8379b55b1ec37bbd425907f07f4311", + "options/license/Symlinks": "sha256:36f07aa8b6ad56853879e759e1d5c6c8b5caf438c728aececeff8e728acf8aa6", + "options/license/TAPR-OHL-1.0": "sha256:b388f10f05822629ebd5c9b0779d3043d1a94e7e88ce7bb6b2cf6a098dc012ba", + "options/license/TCL": "sha256:36fc2d8a56a2a014ded51e298a7e63635eb4ec62e8bedb99c82b892542ac5e7d", + "options/license/TCP-wrappers": "sha256:0645a9c6aa32bbcb9b8576ea237cbf081e9c640454daac8666ef9f9300e629f9", + "options/license/TGPPL-1.0": "sha256:ea4f57efc008f1cc87ff9bf78e49b92ef14ba5ef510d8bac6a83291d2911f655", + "options/license/TMate": "sha256:8596527f4b4ed7aa90489c3fc5ca7939db93c378f1cffa1edba65408f239876f", + "options/license/TORQUE-1.1": "sha256:fa1d8c1d1943bef10710b469f2d0fc95fd790eac9f23f92f805d2e4a07129d2e", + "options/license/TOSL": "sha256:3d65bc3ff37f202d56b1038cb19553a44d114787894daa99472849df8439169e", + "options/license/TPDL": "sha256:9f945905a2cc37e6d7f7bba3be144957d5d95eb75825cf07d9ff24127c29f788", + "options/license/TPL-1.0": "sha256:f24dfa03b06f91bbb24b5d6c60d92654d2cc49be92f300b5407f855596425d13", + "options/license/TTWL": "sha256:bc2c29906ea52dfa77744560479c1f0fc2218fdc6907b8d98ec08df2b77f5e53", + "options/license/TTYP0": "sha256:c45181a527ce9ffc8a63e84dbd49151671b37d5a52888069b8eaf919624fa6a9", + "options/license/TU-Berlin-1.0": "sha256:2a4f230a4eb28fca3c97aeb6e9464b8790a7905a7153458bd488f30b73e59281", + "options/license/TU-Berlin-2.0": "sha256:765c0f8c765bf95b48bd6ab14a10bf06d76941b5079502fe385f847aaf8128ec", + "options/license/TermReadKey": "sha256:2739578644b799505e1f5ca5e63bfafcb0c0da9b3f5a63e9f9f0017947746d0f", + "options/license/Texinfo-exception": "sha256:a40028164e2124a3034146b0eb9e00de829fa3fca38d0acc33dbd77034837fbe", + "options/license/TrustedQSL": "sha256:40599f56c582671a552e64fb616fbb7710f3169be0b4ddd724f80a5546d1cc66", + "options/license/UBDL-exception": "sha256:5be9d2f0268908f2eeb87578d9b730caf6f7b36bdc7a520b1a176f9ac0bf1db8", + "options/license/UCAR": "sha256:fdbc952e38cca091be8ab257ae3770f05fa9ef4f86199bb6e66374e236083185", + "options/license/UCL-1.0": "sha256:81c86c108696105f033b37dc4750a123d05ed684028e3909b1b6bdea9df5a29d", + "options/license/UMich-Merit": "sha256:b3177f6981ab51f225fc54c8deac8c9fa94c1f4b6757a0219078c3d01446a1bb", + "options/license/UPL-1.0": "sha256:2f81e75033a0c872ab9725b36081e363f0c9e6c45585c35930edb66043fdc576", + "options/license/URT-RLE": "sha256:affc7d6f1d5ccf0531ce43ba66615784a3b53311f16a8e957a068049e62e8e65", + "options/license/Unicode-3.0": "sha256:f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1", + "options/license/Unicode-DFS-2015": "sha256:89e104c664787a7e98d6ccb8c5c24fe8033ed75421ccbcfd360222732b5a3ccd", + "options/license/Unicode-DFS-2016": "sha256:1a33fc12a3abb0b3eb14db70e36ef1d0af51c93b1cc1f80f022c3bd406e9b874", + "options/license/Unicode-TOU": "sha256:cb6ba87a4979d8152c726eac13b4480d5bbddd206e0ade0cb91b769e5ca30025", + "options/license/Universal-FOSS-exception-1.0": "sha256:e4ebf1df3a499394b364914467bf088d41e391d8bbc2781ade15d6085f436574", + "options/license/UnixCrypt": "sha256:a3103b41288b9b9831261074cda9be4c8e029b31811fda47ec76d6b59530db23", + "options/license/Unlicense": "sha256:0bdebfeda07d45dada625ae1317c6f833186e798b171d0db640bcf32e92a8240", + "options/license/VOSTROM": "sha256:1c7762c7e3ccc74bd0e4359b05ad33e95d5838099c41e8ce49842f8558546ec6", + "options/license/VSL-1.0": "sha256:435a7448a4719164bd6eff3dd00cef735eb4b81f70b715d8891753ca46777570", + "options/license/Vim": "sha256:2812610ab6a7304edf0282e236f9dd2bd9e47db05f3abed7538c57173d1ed73b", + "options/license/W3C": "sha256:df7429635bacfb82b3e92c2fff50553949afd97389890d53a67b67cbf9fba68e", + "options/license/W3C-19980720": "sha256:8b3984236d53eda13af7f1b01078b33e4390608523865aa1ddbc4083e6492c95", + "options/license/W3C-20150513": "sha256:38b688e653a972897f6a875c53abbf867b261cbeba7ded0bdf100ae9907a6c19", + "options/license/WTFPL": "sha256:13d8825a59e5e546d41e8711405d0f335a9934362a47c04a3b4280fa7932dc4a", + "options/license/Watcom-1.0": "sha256:6aae307240e425f74bdf26242e08e170f701a5c97db796c363200b4a231ee680", + "options/license/Widget-Workshop": "sha256:dde146bdf15c707da85b151b3990104992491368002d65b62e56e1dd25e5a03c", + "options/license/Wsuipa": "sha256:f6c08f21d0b12e77ba730cab5ce6d55c2b65fd01a19c51a7c36134a6c83bc04a", + "options/license/WxWindows-exception-3.1": "sha256:8147956dd2d78744a052bc0d6490bbefe21e18b325a096fb13b3768b74458b55", + "options/license/X11": "sha256:e1963c9958b93bc2578b34f8ae22cfe205e8c0e7d5be7235cfdbdefbf92a5423", + "options/license/X11-distribute-modifications-variant": "sha256:472a5d3bfe4ba4289fbe4d3ff016be25ad4af4c6d7ea523d443139b56d214130", + "options/license/XFree86-1.1": "sha256:f87e1172d544674ad152378494a267e8260005b45ecfaf240af8b3bddae1eac8", + "options/license/XSkat": "sha256:ed84c6b9d91007c072aa95bc50768cd2fe3451eaeba31f824f95698cd006341b", + "options/license/Xdebug-1.03": "sha256:bc2aee27f3f0806be39e329d284ddbbfb14602a49af261df305dd6122d988eb0", + "options/license/Xerox": "sha256:2dc0b8da7071d88b88e4f7b7de338d5339bc44ffb1889d66d63ef9b9ac5e5d11", + "options/license/Xfig": "sha256:515e85563ef3a75e1cd23f6aa9695506c14c5f3789d207d12b28f689e883010a", + "options/license/Xnet": "sha256:55105d933d1afe61d28605a020f065726931fe97e86dd1df5798fba08f6440c1", + "options/license/YPL-1.0": "sha256:345d93c4029b04cade2533b1e2aae012090e055a60b5d4cf1d4a68a5046eeec7", + "options/license/YPL-1.1": "sha256:d519065b0d392c52e9f39eb96a4f652d79af841dbb05382e81d2352caa22719c", + "options/license/ZPL-1.1": "sha256:d384d993a9eb2ec842f15348a862b809d7f1a4d7fbdec18459d6249c8007cdb2", + "options/license/ZPL-2.0": "sha256:f7a42094e22b669aaa352720e6dc93d49da430d8cb25ebf904b50faa3fd98b20", + "options/license/ZPL-2.1": "sha256:3ab20c1a75502ea2231423bf6097a2cf56e336236c1153c2b9a4cb01608fb829", + "options/license/Zed": "sha256:673b1f47ed660463e8bad23e7a1dc14c24847765167629a15e7a3b80601946ed", + "options/license/Zeeff": "sha256:e93b9d8c76e3d6d25e50bf003d8055928adf75d1f2c95b83f6ea87079c9de8ab", + "options/license/Zend-2.0": "sha256:c501915ee7c588173de03db9a8ca105c1baadb16bb05bca211700b850bcb077d", + "options/license/Zimbra-1.3": "sha256:166da4989dcdb774d9592495094b8fdabd6ecc28e3de63661d398b10e859bb0f", + "options/license/Zimbra-1.4": "sha256:d2b8dafada0490c22d39079793ccc6ce4dbb38a9c071ef96152ef1d6a759e511", + "options/license/Zlib": "sha256:bfb1112d49db5b1daecdfef24bd7e2f3ea0bafb33aa67aa0ab51e2bf8407c03d", + "options/license/any-OSI": "sha256:794e2165b96e8e85340c037ea93ae3b5b3a6d30929f711a3b0b7e4b2074f5029", + "options/license/bcrypt-Solar-Designer": "sha256:29b9403d476147aa0ac0be2c3845c187a25a72c67d6250de823e24ae85b224a7", + "options/license/blessing": "sha256:592db1199aab67aafe4515db9808b420db122083807c5329b2f061951e79acbe", + "options/license/bzip2-1.0.6": "sha256:0a56dbabe7d2ff65dea26e8b795fe42c6204a192b974d8e9304fe356ccda9fd1", + "options/license/check-cvs": "sha256:acff39035d2a1ee1eecc3114c43df5012b120eb285b1a3ca69186469c8dcb53f", + "options/license/checkmk": "sha256:30498c160b13f0299079b7158e8caf59f3ee457c17ef1a51ff8c7d9a67904f66", + "options/license/copyleft-next-0.3.0": "sha256:fb1e982dc26f539d1ba48ab2e7ba16e9d573fa9e4cdca3a36c47ceb2b42f8cb1", + "options/license/copyleft-next-0.3.1": "sha256:ef241a58203ce1474141d3b014ff59d5a22b31248263e8dd78f7b5ff367e97bb", + "options/license/cryptsetup-OpenSSL-exception": "sha256:abc9c3364edc1c581a74723ea83094709fef840d8d8a65c731c7303a90a19eaa", + "options/license/curl": "sha256:272d1239bdcef73bb98bac206168ca1040bd4a31296755fadfb7515bcea18df3", + "options/license/cve-tou": "sha256:16c75e8640dbf131e7325c688a534b2cf751ab4550e1028f4a2ac0910f5ba903", + "options/license/diffmark": "sha256:44c1eae63a550696742e0896b6122bd65bdbeace0d9ad64d24a3fb05100563e3", + "options/license/dtoa": "sha256:8222bef21419a8734583669c5d94ebe557f8f6309c8c53fcc0bb49953bbc790b", + "options/license/dvipdfm": "sha256:b80c6a40accb70f26e04d26d47db8d9a885796b47ae6d027256fc1d4b7004d4e", + "options/license/eCos-exception-2.0": "sha256:44592e1aa370972fce06f0d2487e69e1f2c3d601a0526067f2660bcc0c14d8ef", + "options/license/eGenix": "sha256:fc27d513401d457ae1d259f8714f9deed874aa35d733ed719a9f4248fb5e3e68", + "options/license/etalab-2.0": "sha256:f82596bde5c4abc62b4e63b1a3ba555f6b9203a340765fb178d188958600d111", + "options/license/fmt-exception": "sha256:4768d807baac73127f8d1234dbb17ad766e243d62eadba17e507742f70a9555f", + "options/license/freertos-exception-2.0": "sha256:f8e6f210132c16bbb9eb4080473280134d418ae05f17bce1096bd3406aa6900e", + "options/license/fwlw": "sha256:a133cffd1fe5246ae30fb8a14b9e6b68ef2bf2f3f4ebacf73a1d5d3b739a6124", + "options/license/gSOAP-1.3b": "sha256:1ee77551e0154daa4e2818fd388fe50bf6176b005e991ad8dde7dbf6ccb3b364", + "options/license/gnu-javamail-exception": "sha256:b84fab2e47558745f5b77c7047a0178f13d44cbcd9a96f08bc453e2bd64cb609", + "options/license/gnuplot": "sha256:bf8f6d32101126a5994902c3b66d32c4cdbc1202abe13d6c4ac86d970e7c0a9e", + "options/license/gtkbook": "sha256:1ead41c862686ec7d2a95781027749e349de3df5446d5a19806ba23de05cc2dd", + "options/license/hdparm": "sha256:72760f4bb9d0f4ae7630cb80b915823012702de1701688c5c9d38f9c74adee1a", + "options/license/i2p-gpl-java-exception": "sha256:fc6ba35632afecfa4160f6035c4bb52c51347cd71a4ad34c4f5a6f7d8a475c5b", + "options/license/iMatix": "sha256:b671c84dec7bc37711226b9db9da9a6cebaad6bd006df3d60f080706181820ac", + "options/license/libpng-2.0": "sha256:f207d0f7375b977f76ce2437962c1c12a646f15cda3df48d5472ac95eb68e8ff", + "options/license/libpri-OpenH323-exception": "sha256:a5b76fad3e9e4cdcf0d980328309295cfc6d1dd8f66478ff54f0385a6ee3516e", + "options/license/libselinux-1.0": "sha256:86657b4c0fe868d7cbd977cb04c63b6c667e08fa51595a7bc846ad4bed8fc364", + "options/license/libtiff": "sha256:a6ecaa20c8c1b7a8215ed05e5f58764f821596e36d31acc05282c6154cf0dc44", + "options/license/libutil-David-Nugent": "sha256:b389615248f34daef9db640576d666ef01cb81852226d74f070b73e5d86eb7de", + "options/license/lsof": "sha256:225222ff9bf38abc2cef7205e9b05c60935efa9ec28d1fd01e503c9316088718", + "options/license/magaz": "sha256:5b446f36f88c0ae31a51a3eb270bb09d247a9ef4e5224e01101f77e293b06e85", + "options/license/mailprio": "sha256:d043a14b0f0584889194a46371243ca47ab9ccdcef0e719579939cb78f85da33", + "options/license/metamail": "sha256:a4b1dbdddeccbaad4687f2777ef43222eecf29ba4d9b196b1e46d11a2d57f008", + "options/license/mif-exception": "sha256:bd674fc4a77b6a474a2e94e85b5b9fcbfa87391c7885ae22415a279ecddb90dc", + "options/license/mpi-permissive": "sha256:3f86e2f5f1cbe019e0738fa89b2fab321015b3e5e2c0acd6649680f38fc9cf75", + "options/license/mpich2": "sha256:a284ae7293a108411704b2776064ac81a796ad2242813b8e777e36ede376eec6", + "options/license/mplus": "sha256:c52bd7a2a72924c78c590908416345934a7e139cde62f3141533c2e500ebd4ff", + "options/license/openvpn-openssl-exception": "sha256:0b57e6db161b5f82667b6d0d9b69fab7052b0ce946880f5a5079ff05ed5dcb66", + "options/license/pkgconf": "sha256:a1a5488eb4a3d421514847fdeff4a1a71d363629b145f42ec9a3b1d7151e114b", + "options/license/pnmstitch": "sha256:b8f88b9690b1eaf8af0e99a0f9491995b5632a87a91000f14f8b6baf4ddde0c2", + "options/license/psfrag": "sha256:b14894fe36001fc867fb48736604e34223b2565448e63fb22476eb8fc02693ec", + "options/license/psutils": "sha256:515a50442da639fa0d82d002b99a95cd087b9ced41ecf49a0160f0695c1a9571", + "options/license/python-ldap": "sha256:8c5c1b80c0ecdcfc9379de912c82f6f5fd32eb1d63877e94f929fe307917bb70", + "options/license/radvd": "sha256:26ed43c573c7b7f796464acf61b190ea39951acc9b0f3c9961ebb96c31e76be5", + "options/license/snprintf": "sha256:f91ffe18448379c5dc14d84a8285513cde4ca65700abf757a1737a3084526fd9", + "options/license/softSurfer": "sha256:dfc0543453cfb9d0a86cd15697fee05d0c9290ff45ba122f693467189e9184fd", + "options/license/ssh-keyscan": "sha256:a6572362c496d6ac52f9c6bef79448cb3f689ad21ef8d250cff859addc251360", + "options/license/stunnel-exception": "sha256:9d9536ac2a881b5ca24756a338b12f3bf9f213287b2a065b0e23eefef4bdd5d6", + "options/license/swrule": "sha256:7db3ae990965d1d5f5baf6bff4aa4603cbc84f84e2d4ad2a29847ac0dc8c79aa", + "options/license/threeparttable": "sha256:4a4f8efe9981e6736dedf8330b8674ea65cc3ec47749a7f37f9e97f17f0f7c5a", + "options/license/u-boot-exception-2.0": "sha256:115b21a0c04cb7f63c81400562e30dcb56a0f7c5be0ddecf616448f6176d04fa", + "options/license/ulem": "sha256:6bd02304617413a1cdec3a14227e995500f779241231224e7c4c5430620506bd", + "options/license/vsftpd-openssl-exception": "sha256:e75418ebdb3003280c6d38a660b62d3d1ad9e6013a2557740127102de1a9c01f", + "options/license/w3m": "sha256:1ec6c72eff7efc1f806e532ce1fbee17f5051c898d9d594e3ff24cbbe4c3065a", + "options/license/x11vnc-openssl-exception": "sha256:c5f04dd88fd00ad6025979555a55919a18ae08f254c535aac071164895406de7", + "options/license/xinetd": "sha256:7b3cf46961d23cf845ac7c52075693b6ca31389e1534c655333b4b6fe0459bd4", + "options/license/xkeyboard-config-Zinoviev": "sha256:7a1b77840b6a7299c539aee677d500b68e332fce28466a90a7c43b25037dab04", + "options/license/xlock": "sha256:b8bbeada89cb533dfbabd24fa6eda1de38155fd4d5cbc8928796d917a3411878", + "options/license/xpp": "sha256:7357cebf92b4a7b3667e81c15a56eb1e0b4c61f8161c1e6faeccab22912a9879", + "options/license/xzoom": "sha256:ee33b2ab24a56434a9ed78020f48359ea2916afe44d2b21c88ad435e5263701f", + "options/license/zlib-acknowledgement": "sha256:aeb83cf7f4c9076691f72856d3b8f64a152027e9b1a136d399198c683fd26c12", + "options/locale/TRANSLATORS": "sha256:35f8a4cde27510904cd825c0ba9fc9152c49548d9169aca1d1b9329a5600b24c", + "options/locale/locale_ar.ini": "sha256:aa1afbda2fc1194b40b3e1c48836f43ea49079b3115a5679245de9e4c5acc5ca", + "options/locale/locale_be.ini": "sha256:d77bf05216f96d59442df793ef3c905c2545c403213a0a860a9fbff26a15101e", + "options/locale/locale_bg.ini": "sha256:f79beed43c8bc7f6fc73eabd0503b40f02cd789d6cd0d32ab8be7a25ef414be3", + "options/locale/locale_bn.ini": "sha256:ed6506d2f6c6fa1919e7eb5210731b0310281ff0814d44374adf931fef307244", + "options/locale/locale_bs.ini": "sha256:f9d8eaf59d37866bedd4ed67ffedaf65c01086ca13d02c9680639e9bbf09d440", + "options/locale/locale_ca.ini": "sha256:94f7d909ada1b54c7a16284fceaaee17a2a32d287416e9bc4d15c11162322c22", + "options/locale/locale_cs-CZ.ini": "sha256:5ac52fe324911b47ab24c07f9a3960739443e85d24b2b53e13569ac626474f6e", + "options/locale/locale_da.ini": "sha256:de48b77f9416d4b73e43dc7453501ba8d552b20da1a8c799ee8b85103a852280", + "options/locale/locale_de-DE.ini": "sha256:c719cc61607e0bf1f91e8750ad4b44abeba291980ac0912c813c5abdf0341770", + "options/locale/locale_el-GR.ini": "sha256:4a932d4bbe4c5ae9fb2ab0c6ce1de412b644b62e312039bb09b235298556ae0d", + "options/locale/locale_en-US.ini": "sha256:e1ede4adfcbd33ef20df38c28203e1bbb55ef636f9b36e9ac58cb527ff431ef2", + "options/locale/locale_eo.ini": "sha256:c2d8ca030d470ad1b64108ed00ea3b24a79670a18fb161b6b7e9ff467b16f90a", + "options/locale/locale_es-ES.ini": "sha256:b331a7da3121a376b1e533e27b7ea4448230779f126bafe829c916684c1f0a9e", + "options/locale/locale_et.ini": "sha256:bf1054d4f2c2bfd99ec33d474db91ac6e8d184cb726f81ba1c5ef68c3923f318", + "options/locale/locale_fa-IR.ini": "sha256:1ba19689e6076f1fce36a0c57eb8415654aebfa44e9b9136ec94f1aaf41ae8b9", + "options/locale/locale_fi-FI.ini": "sha256:7b6fa84e47f4a2f7d67de0d7043eae9fa7fed9900b086a2beccca0d423c66db3", + "options/locale/locale_fil.ini": "sha256:33d27e0d5e60bfdd9d51a6890d48eb1f808b7067956000ebc1a4e8e62a1b7c6e", + "options/locale/locale_fr-FR.ini": "sha256:1a6352e856f0496bfd426e6a3a359e1e3a0112a512e685d2a21fecc4b228d97c", + "options/locale/locale_ga-IE.ini": "sha256:67c9907b40aceee9fc341bad4236ee35a73343ce01365d08962be560ebc21344", + "options/locale/locale_gl.ini": "sha256:700303fbf846fec09312a5717002ca06e9663099600b0269c8b8bde7e02f8ae2", + "options/locale/locale_he.ini": "sha256:7ee1a3ebb2817eab865272d17c5259bd7f777a7752c860e2db71a885cd808547", + "options/locale/locale_hi.ini": "sha256:6452373f5d27c58851ceefbe135b3d3640a8ba0357dc0dc14036075d91db093f", + "options/locale/locale_hu-HU.ini": "sha256:56e06bb7c1d572023ba3f044f6135bae278c02209f9498571faa18d757cba906", + "options/locale/locale_id-ID.ini": "sha256:ce01deb11b59a0b77363298f4caef06b8a48f5ff31727438fc8f33d45bdc7430", + "options/locale/locale_is-IS.ini": "sha256:59033c401976344a63ee5bd0bef0682b4a5e5637788bf63352b72f8937892925", + "options/locale/locale_it-IT.ini": "sha256:f56d4c1c0a49b1d5a857c543b94e2939a0beae128927405a2e1d7f178e54e01d", + "options/locale/locale_ja-JP.ini": "sha256:759b748379e26b3f81e3203c01f0c79504eeb09069efbbd0ba52a3b0d6d06a0c", + "options/locale/locale_jbo.ini": "sha256:bc41eb11921f085c4d689ba3538d84f6c90113f362116691804c2d26e6cef966", + "options/locale/locale_ka.ini": "sha256:832500c89b9570281e4d16f029874a008d3be6c65521bac8ce434683cfe44a6b", + "options/locale/locale_ko-KR.ini": "sha256:586fc87ea424859f2cc63bedf94af52676dc668d44c62b18858e8e0f0d72c7ff", + "options/locale/locale_lt.ini": "sha256:9836168e6d59673acef57967f3f5aa1115a0beedd45292b2b13fae18959d83f7", + "options/locale/locale_lv-LV.ini": "sha256:04a7e9ab94da0e786203c9ca15abcd531652ee8117be50c5583e0d70d9dd0560", + "options/locale/locale_ml-IN.ini": "sha256:b9abbdb514aa2de8a67f3675e6dc1e5a038ca2f1abf67dd46fbb9a5bcbd3dc54", + "options/locale/locale_nb_NO.ini": "sha256:652d544511b04589c767ac68e228ea853b1daa783db04729a3d4a08f6069e46e", + "options/locale/locale_nds.ini": "sha256:e52be594f0cf41ee4ceb8eab18643b2277a95e97d331e0fa0b352e1bbc369182", + "options/locale/locale_nl-NL.ini": "sha256:a4498bf86b34e80881f41015044623f72bd542795e11a38ffe1d2cc25829a3c2", + "options/locale/locale_pl-PL.ini": "sha256:f608b812527c0017548a603bd81d2785015bc86ab15b6d3ce51bd6788b09ea4f", + "options/locale/locale_pt-BR.ini": "sha256:72a35aae4e276213bfd52203f4dacf9da1cc654975951a6b0ed61700daae7c54", + "options/locale/locale_pt-PT.ini": "sha256:ada1b42e51cc96d1b12619016c128220c92c337f41752644b645cff0ceaf06ff", + "options/locale/locale_ro.ini": "sha256:d9751bbc366b5acdc6c7d09f0d841c46f5e93ee84b54c403878785fd5f8f72c2", + "options/locale/locale_ru-RU.ini": "sha256:e70e6b654baab179a707e543c09372af0f403d2b58f170744eeb8fda83b32ce4", + "options/locale/locale_si-LK.ini": "sha256:4cadcf59b898f74c5b4e5ad8c4c39fb077465410b937908c90986a77d1df96aa", + "options/locale/locale_sk-SK.ini": "sha256:0c46ecab5cfcae18a7545533bea09d87c918e2a69b8b318057c36bfcc181c57c", + "options/locale/locale_sl.ini": "sha256:8e572b4b92eb91e9c42e94c30aa8f9eed54486068cb4d977495efa66c852d11a", + "options/locale/locale_sr-SP.ini": "sha256:11c647be60459a589fb4a67bf365117204e205362abf3d59fe3eeda57e77997b", + "options/locale/locale_sv-SE.ini": "sha256:71a62a6c29c8a46504d75c616796e188f5accffe0f0210db47495c0c88e3bb83", + "options/locale/locale_tr-TR.ini": "sha256:3b8d42b367ca96c4c8f27340022c69e48d5285f70fe8a0de799ab8df42726f63", + "options/locale/locale_uk-UA.ini": "sha256:e869f84b57b05629709faf7faae32b63e5aaf4a321897d7fa7f84e70056516df", + "options/locale/locale_vi.ini": "sha256:b41c26ed0187aceb498d2258bd32d916234a4b23356a2e3ae3427ccb155c7b34", + "options/locale/locale_yi.ini": "sha256:e73bcc11319afd05879d137c7a995273eeffa304beed6c34dc6b7f431d710500", + "options/locale/locale_zh-CN.ini": "sha256:b52185888394e097422d75ae27b82bd03d06515c0a6bea41a5706fa0abd32043", + "options/locale/locale_zh-HK.ini": "sha256:074c2bf94519b3f49ca89745a48a8ca45bd411ab648c6d7480e87fee76763090", + "options/locale/locale_zh-TW.ini": "sha256:7a12bfcddf69b3e97ba58dd05534917045b71194298216eff878cb80db6d68c3", + "options/locale/readme.md": "sha256:5420a9b8acbddea90a96c509d507c0f51cbd0a0603f79307b83bece8608fbaa0", + "options/locale_next/locale_ar.json": "sha256:fa1d2d53bb5506178d802ffa290b196bb2561cc5303ef42640e02a905271811c", + "options/locale_next/locale_be.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_bg.json": "sha256:b6b2f50b66fa79b066a80c88078dbc56bbbc213d87199ce4994548d13b12a3a2", + "options/locale_next/locale_bn.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_bs.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_ca.json": "sha256:f56fb22c192e5a285f3d6cfe12db2a9a1ffa898fc148d2f728e9c45c63b161de", + "options/locale_next/locale_cs-CZ.json": "sha256:e9a251a135495a76a60cf5e53f9a967dab5dc18bb655a3fde6865805f0798121", + "options/locale_next/locale_da.json": "sha256:f402010ce7ba6603612ca6671e8b0b2debbc41f778d19bfd97ecfbee0a54779b", + "options/locale_next/locale_de-DE.json": "sha256:65c01fa887f2eede448251ff7c355e0553ad403d21fdd21d116e9fdaca55d094", + "options/locale_next/locale_el-GR.json": "sha256:25aadca4c2f59b9cc2a93b74e720fc835a472602a9c63538201de2dfc3a29dbe", + "options/locale_next/locale_en-US.json": "sha256:f4fb16f94f7c2ade4870bd86105583a20b5d6e85564ca2fe8db519c01f092483", + "options/locale_next/locale_eo.json": "sha256:bbe5ad116ceb7b08d2573ebc3765cf2daf95c36be44bf1abaf865c100fe7bdd2", + "options/locale_next/locale_es-ES.json": "sha256:7cb37451fbab1e0f70aa65ac857956baff8ae9d400b7310f1e1c7a2b9e614180", + "options/locale_next/locale_et.json": "sha256:80aa094d806cc370720f896c7d041de5a1ca1efbceea170de192b1ce3572682f", + "options/locale_next/locale_fa-IR.json": "sha256:fc014bc2994cefc7e4422ce2846fbc67057993ef62f17bafaa9c68eb39921f37", + "options/locale_next/locale_fi-FI.json": "sha256:8914d9fada64e971e2bb6d1b8b9c2ab088351f72b023980fd340f259cbdb3572", + "options/locale_next/locale_fil.json": "sha256:71a8daa9326f93e220d195825eb221cd3e7304a6dcdb614720047b7780ae1bac", + "options/locale_next/locale_fr-FR.json": "sha256:54289ff340465fefe05f6ebc9991cfe1483e08e77f28b66885714be87ff1d91f", + "options/locale_next/locale_gl.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_hi.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_hu-HU.json": "sha256:36910f273df404ec6ae7d5ad3fe4e14660dc9c3ad79c57cb2cef7bc519d5f5bb", + "options/locale_next/locale_id-ID.json": "sha256:896fd839e4aac84145a48bf56bc265e71f8bc8aaf9b8ec548d0c57c64187b956", + "options/locale_next/locale_is-IS.json": "sha256:19c794004d6bdf2e13f241c82c1e4bf9b15e45f00ce0c20c27a42c67b5f058df", + "options/locale_next/locale_it-IT.json": "sha256:dff31612af80226a70e2a158bec255b2102ca57844eae666f886aa0237df2732", + "options/locale_next/locale_ja-JP.json": "sha256:dc18dc3c62d2f46ecc910b12620bf3413e886c17189b370568fd652229017508", + "options/locale_next/locale_ko-KR.json": "sha256:f1a84b377598b29ec35bd13eb794268baca6796a5fbd4e36f3505da75f9e1ccb", + "options/locale_next/locale_lt.json": "sha256:a7a8683912e909b87d7b4737ec2b1523abab4b0540a7db4e745e7be05796b0bb", + "options/locale_next/locale_lv-LV.json": "sha256:e19091f251cc91a2f32bdc3ca4127e8773e4eb7c0eb0955bba6cc0ffa6eb901b", + "options/locale_next/locale_ml-IN.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_nb_NO.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_nds.json": "sha256:5d1c73f73120e50cea12b418b8d66feb75f68fc98536712af63d101b1c1adcec", + "options/locale_next/locale_nl-NL.json": "sha256:980b98030cbd5fb464618f03ec25386e55b8e858199aed5b64c5fdfcab164172", + "options/locale_next/locale_pl-PL.json": "sha256:22ae3b6057a50ba48e8bdcc82e6608bd5ab50aaa00c15e542e72642236486368", + "options/locale_next/locale_pt-BR.json": "sha256:8355f82a8f68aa819ab5feb89c569e089c9bdac7ab6f357884f9600191b728de", + "options/locale_next/locale_pt-PT.json": "sha256:34ddb1d1fc521ba768f3093081f26a2a631b33b7306456917a628083bbc499f5", + "options/locale_next/locale_ru-RU.json": "sha256:2e690bbe198ddfc3d3e3a31d6cb6acb4a491f7c1a43567eab88e9c0c87eadf8a", + "options/locale_next/locale_si-LK.json": "sha256:ab79f178582f3df583ed5b270c8323bb41d4c95a4a35c62fc616a2a559f119f0", + "options/locale_next/locale_sk-SK.json": "sha256:b804ad86e45f371d284093a160787c9a8560d7bbee7a7062bd5d638875d74822", + "options/locale_next/locale_sl.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_sr-SP.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_sv-SE.json": "sha256:8799c35bbfc37cc3055ab29cfd7af53a8e9faf77f4dde7a04622192719da3616", + "options/locale_next/locale_tr-TR.json": "sha256:09c6d045dc010654ff3a10de398e7db7fec5b3ef21587b4a10eb3c20f853cd7a", + "options/locale_next/locale_uk-UA.json": "sha256:bedaa20155fd15792a01a38e0be2adfb65ea1aeeb970b369037bad0ddf06c931", + "options/locale_next/locale_vi.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_yi.json": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "options/locale_next/locale_zh-CN.json": "sha256:fdcf3e1d2c528208faaa8b771d8cd51c8dc07c2b4f7195a8fb72e11882b0cf61", + "options/locale_next/locale_zh-HK.json": "sha256:5f0da8ffeb42d552beb23794d7305a25bf2b8e990d2c3a8d4e756c6fbae608a2", + "options/locale_next/locale_zh-TW.json": "sha256:29cc18f4f6ff338dd6f5022f858ff486140358143cc68076e43f8bd9f4a656b6", + "options/readme/Default": "sha256:65db8c8f386fc251f8e981a90bc023211d91b8023271e54f00af6dfaf6286191", + "public/.well-known/security.txt": "sha256:5f729a0f12354e9a8d3b819345d2b81abbcb9839ddc8cbf2b9ec701f3b9be4ad", + "public/assets/css/asciinema-player.41ec509e.css": "sha256:fd9104ff04e811bb7b79c8a6ade062c1c78f5d71591d08fe76dfa17fc994e739", + "public/assets/css/code-frequency-graph.55afbe7d.css": "sha256:a2147d6272c2440f7c2fbe57069620f891cb7c8b5a5ba2429df1f05b66a61998", + "public/assets/css/colorpicker.ec78e358.css": "sha256:3c17557c3ba5beb9607a20b40135b09b675c1834759aedbd4d2fc61c3047f065", + "public/assets/css/contributors-graph.971702d3.css": "sha256:9233f8011df4fa17cdefde453c34409d2064ad5e6cdff9b6f3d52c5bea24820a", + "public/assets/css/dropzone.5a752d14.css": "sha256:9d7698738e984891dc4722b939468fc14c705d2666ae201b7070afb70b0851a2", + "public/assets/css/forgejoswagger.css": "sha256:b01458a978f481ca5cb795f36a60b72d883ed04cc0bf00313e07aec090b0f2a4", + "public/assets/css/index.css": "sha256:0e46bc9c08c1bd649ee62286cea466296d4bda91661ab153e2b0f1b98dc1673a", + "public/assets/css/katex.41d5cba5.css": "sha256:b3dafbdb52f2de0c7ebd0b28061f53a65703c28b11e7dd12a20c22a92da8653f", + "public/assets/css/monaco.5612f696.css": "sha256:418c84514187fb221399e563d6cea988c0284b9347e5183e286b2a17011bbc8b", + "public/assets/css/recent-commits-graph.45c55234.css": "sha256:2d489db8408c346e7929c0d29a35699089098b2737efe2c0001bfb59ca4c5496", + "public/assets/css/swagger-ui.9aeafbc4.css": "sha256:878046c4002239f92d90107ec09e115478a2a93749debbb18e175e9a5f041e95", + "public/assets/css/swagger.css": "sha256:b01458a978f481ca5cb795f36a60b72d883ed04cc0bf00313e07aec090b0f2a4", + "public/assets/css/theme-forgejo-auto-deuteranopia-protanopia.css": "sha256:6004f6fd517dc42e6dee00eccb5749f317bef1ba2a514726459947bf246463d1", + "public/assets/css/theme-forgejo-auto-tritanopia.css": "sha256:c9599e4cb259f16b8ed1de5aa6d89a0c6b4fa8c5e4051158a1f2874f98d69c0e", + "public/assets/css/theme-forgejo-auto.css": "sha256:2115fb67737576723b01297c60c0464497038822da9b75da48250af37a51ad46", + "public/assets/css/theme-forgejo-dark-deuteranopia-protanopia.css": "sha256:139e9e8daf2b95d2c9a3609d71b452e23f32bb973296228f8f1604928f0743f9", + "public/assets/css/theme-forgejo-dark-tritanopia.css": "sha256:2f8ba31bdaa378e3608624bbee4bd2fa37b01d6e3cbf25f6dc7491d3087102a1", + "public/assets/css/theme-forgejo-dark.css": "sha256:9a2e102333ca5b59c733cf14e1b38d9e1eee9d1903edb603b17887380e721ab4", + "public/assets/css/theme-forgejo-light-deuteranopia-protanopia.css": "sha256:377bf3fa922a8745dfa9e5fa4f1ceae1ea0e855714a16df9fbb9dce0c990db2d", + "public/assets/css/theme-forgejo-light-tritanopia.css": "sha256:9172d48af2534cce66fd5d13146a47a7c683e0cff9456bf6253fad725c82a6e8", + "public/assets/css/theme-forgejo-light.css": "sha256:c2799bad5ade828c542040b933de2856b5ba7cb265034fa57235796e97b4fa4f", + "public/assets/css/theme-gitea-auto.css": "sha256:c22345cafda952670ac22dddd70106e4ee0107a0399cd2f46d0655851a9d6290", + "public/assets/css/theme-gitea-dark.css": "sha256:307003bc194d1b73dd86c197de126b8fa62a2d801f93ad7387d085b31b2c229e", + "public/assets/css/theme-gitea-light.css": "sha256:63953e717301b70fd744892478c6cd5940101f1a067b7b5f31cc6d48eab4f04c", + "public/assets/fonts/KaTeX_AMS-Regular.73ea273a.woff2": "sha256:0cdd387c9590a1a9f9794560022dbb59654a7d86f187aa0c81495ad42d3a7308", + "public/assets/fonts/KaTeX_Caligraphic-Bold.a1abf90d.woff2": "sha256:de7701e42cf1f4cf0b766c03fb27977207eee2f4fd5d76fa82188406da43ea4c", + "public/assets/fonts/KaTeX_Caligraphic-Regular.d6484fce.woff2": "sha256:5d53e70ad607c2352162dec9e0923fb54ecdafaccbf604cd8dcf7d00facb989b", + "public/assets/fonts/KaTeX_Fraktur-Bold.931d67ea.woff2": "sha256:74444efd593c005e3f4573b44524704c0af0a937fe911cca9e94068d0d140d3f", + "public/assets/fonts/KaTeX_Fraktur-Regular.172d3529.woff2": "sha256:51814d270d06ff0255dba0799994fa4d8c84d11f09951d47595f4abb1f3602dc", + "public/assets/fonts/KaTeX_Main-Bold.39890742.woff2": "sha256:0f60d1b897938ec918c8ce073092411baf9438f6739465693ff18b0f9d20b021", + "public/assets/fonts/KaTeX_Main-BoldItalic.20f389c4.woff2": "sha256:99cd42a3c072d918f2f44984a807cf7aa16e13545fd0875fc07c6c65f99e715b", + "public/assets/fonts/KaTeX_Main-Italic.fe2176f7.woff2": "sha256:97479ca6cce906abc961ecac96faa5f9ca2e61b8e7670d475826bcdee9a7c267", + "public/assets/fonts/KaTeX_Main-Regular.f650f111.woff2": "sha256:c2342cd8b869e01752a9321dc17213fc40d4d04c79688c1d43f2cf316abd7866", + "public/assets/fonts/KaTeX_Math-BoldItalic.dcbcbd93.woff2": "sha256:dc47344dbb6cb5b655c8460d561f4df5f501b90c804ad3c6cec65fe322351ab1", + "public/assets/fonts/KaTeX_Math-Italic.6d3d25f4.woff2": "sha256:7af58c5ec8f132a2ddde9027c6d7814decce4d3b822a11192a42a20e2e973264", + "public/assets/fonts/KaTeX_SansSerif-Bold.95591a92.woff2": "sha256:e99ae51144bf1232efcc1bfe5add36262c6866b0faab24fa75740e1b98577a62", + "public/assets/fonts/KaTeX_SansSerif-Italic.7d393d38.woff2": "sha256:00b26ac825e2095056396e0553b8ac26d3f8ad158c3826e28b4c45b385c4714a", + "public/assets/fonts/KaTeX_SansSerif-Regular.cd5e231e.woff2": "sha256:68e8c73ef42afd3ccec58bf0fba302cce448938e7fc020a5e31f8a952eee1342", + "public/assets/fonts/KaTeX_Script-Regular.c81d1b2a.woff2": "sha256:036d4e95149b69ff9bcc0cd55771efeb25ffa3947293e69acd78d5ac328c684b", + "public/assets/fonts/KaTeX_Size1-Regular.6eec866c.woff2": "sha256:6b47c40166b6dbe21a5dfca7718413f2147fd2399be1ba605d8ad39cedf25dfe", + "public/assets/fonts/KaTeX_Size2-Regular.2960900c.woff2": "sha256:d04c54219f9eaec6d4d4fd42dfb28785975a4794d6b2fc71e566b9cd6db842dd", + "public/assets/fonts/KaTeX_Size3-Regular.e1951519.woff2": "sha256:73d591271b1604960cb10bb90fee021670af7297017e0e98480b332d11f51995", + "public/assets/fonts/KaTeX_Size4-Regular.e418bf25.woff2": "sha256:a4af7d414440a1c1790825cfb700cf9cf43b0f2c4b04f0ebc523011ad9853ec0", + "public/assets/fonts/KaTeX_Typewriter-Regular.c295e7f7.woff2": "sha256:71d517d67827787cfabdf186914cc3358eda539e37931941f2b2fd4a21f68c0b", + "public/assets/fonts/codicon.f6283f7c.ttf": "sha256:0f1d5219934e96e83b8db162d60b4d8c09b5de1e7d38031cbafe4a3c0f2889c9", + "public/assets/forgejo/api.v1.yml": "sha256:7a23e5bd7ec28eae38773c7afa1e5048210722c17aee94c1b3249445974ecf08", + "public/assets/img/apple-touch-icon.png": "sha256:69dfe2be34afbcbf0b8a203b879cea47b6c7fa176340ef45a46fc9fe28ed9d3e", + "public/assets/img/avatar_default.png": "sha256:104e8d2d724463c45af00f64f5829f335a36eb853390787576a78c09feb63703", + "public/assets/img/checkmark.png": "sha256:8efbdbc150b11a887065909a3bce6742f23b3d2a7b11dbd8ea469439c855830a", + "public/assets/img/dingtalk.ico": "sha256:bba9e5a1933215aad0857e7a40881fdf74e92f29030da83d70f771f10f9129c3", + "public/assets/img/discord.png": "sha256:30304c4e8261b43e4d08e0b44ed94a8bd03293c9aec3d61f85245a38cab11e3e", + "public/assets/img/emoji/codeberg.png": "sha256:04fa7727720d7b2bdb393b3ebed8f590a00eb333f9042bb6c9a6e222ec025f81", + "public/assets/img/emoji/forgejo.png": "sha256:104e8d2d724463c45af00f64f5829f335a36eb853390787576a78c09feb63703", + "public/assets/img/emoji/git.png": "sha256:2ae2ceca52e11fd7ff8efd4fb3e60c053063bf99fc008b7acfe320c1db440325", + "public/assets/img/emoji/gitea.png": "sha256:4af4602b9c4ea6d0f03f2ab435fd552d39d771866e6a7c11dc99f6ec197ca9b1", + "public/assets/img/emoji/github.png": "sha256:cdbeb6b8f935c81216443ebc214b607311f9fd6af9014b9043478a5fe6e08dfc", + "public/assets/img/emoji/gitlab.png": "sha256:dfa3c29975fb4d7d099253f65565f69e5fe50e507223b8e2b8db2ae0b871374f", + "public/assets/img/emoji/gogs.png": "sha256:6b350ad9d8ad76df9dc6acae563f7d34fe157a7fdfe364153c90a603bcc8658b", + "public/assets/img/favicon.png": "sha256:caf16ae5198b399f5e2e955fb2d0bfa4547954c375aa20a0394f5d92c805a383", + "public/assets/img/favicon.svg": "sha256:997bea1b94c9b40a4f59d45b127119a51b364edb7d9d689045d4a97876739e89", + "public/assets/img/feishu.png": "sha256:b32b79443cabe1d0e7eccc6b4411081140f30549651f904257e7c9d85ed9d999", + "public/assets/img/forgejo-loading.svg": "sha256:67a06cbf32f1140e4ebe0719a47380dc944852c98b65f3f04de18767d0a84a11", + "public/assets/img/forgejo.svg": "sha256:997bea1b94c9b40a4f59d45b127119a51b364edb7d9d689045d4a97876739e89", + "public/assets/img/gitea-original.svg": "sha256:ad861e06c5004644a50f28f0fe8a5027733d4f184c1509f3fdd749fefac57084", + "public/assets/img/gitea.svg": "sha256:997bea1b94c9b40a4f59d45b127119a51b364edb7d9d689045d4a97876739e89", + "public/assets/img/gogs.ico": "sha256:f3b46f4702482a695605ea54625cb16749dbeeb984d09de14d794e71c41e0d70", + "public/assets/img/logo.png": "sha256:01cc1b23e85e90add3a23be598f9893682d668e154109a00e507a13de1224c29", + "public/assets/img/logo.svg": "sha256:997bea1b94c9b40a4f59d45b127119a51b364edb7d9d689045d4a97876739e89", + "public/assets/img/msteams.png": "sha256:8fd35de6740adc56f6d75251d7751072caa0d7bb8800a22de8b5ff2a06293f84", + "public/assets/img/openid-16x16.png": "sha256:bb9fdb9bf57b4d608d0a1e95fcb5a2642564c578739d717e8fe2ce7d968d0d10", + "public/assets/img/packagist.png": "sha256:274254c5a1a97d172580b92d1392a8901bbc40ade87fdd34c86259bdec52108a", + "public/assets/img/repo_default.png": "sha256:af8a4841bff3bf0db330e6f813108838ca03a0092db4f057d91f03b668b984a5", + "public/assets/img/slack.png": "sha256:810a570e63236a1871dcaf19aebc8df624fbb90e3dc917afd1382f5f0ec022bf", + "public/assets/img/sourcehut.svg": "sha256:451e2e31a33531b8d3dac5a67b0e784d886bb0d075bc9c222229ef395c9225be", + "public/assets/img/svg/fontawesome-openid.svg": "sha256:346be7ac397f3be76a3d13cb583e535bfc4005c3985addfd36a87c1b05ff7cc0", + "public/assets/img/svg/fontawesome-save.svg": "sha256:e7abc3ce5fe59d308ea5bc1ab126f9effa341782c6690721d3d9a14a4055ce71", + "public/assets/img/svg/fontawesome-send.svg": "sha256:fa1d874581b6eacd09f7a71ce08394f1002fcaa90c17984790b144f2fbf64b1d", + "public/assets/img/svg/fontawesome-windows.svg": "sha256:eb2a3c76c4b8a22725f4824c992c69ea8b667a3465267af27d8fddf1c6a4c974", + "public/assets/img/svg/gitea-alpine.svg": "sha256:8acf660ad727e400bdb401e69b3b973b84e9cec93d51ae54800ee1e323a00d27", + "public/assets/img/svg/gitea-alt.svg": "sha256:937d7c2017eff8f1f5150dc82e327cf5a9e51329c331281593e3ec3e6e2d4fa7", + "public/assets/img/svg/gitea-arch.svg": "sha256:bb512b56246d24207fa5ffeaae6e087a061eb14029d3a9aa3d900dc8c65a993d", + "public/assets/img/svg/gitea-azuread.svg": "sha256:1ec9087b5303b44073136b7a976a26c10c4e341051057b0a4dab8e2727f84fdf", + "public/assets/img/svg/gitea-azureadv2.svg": "sha256:836d4716854455c0d6c730a49da342c67be6127fc55d50ac291d9aa860f97615", + "public/assets/img/svg/gitea-bitbucket.svg": "sha256:c6602364979470b8c8b3af81d3775af022fa143f5a4e2c790bb9d8ad73f355c3", + "public/assets/img/svg/gitea-cargo.svg": "sha256:a40930e500224d9e9d47bbf4f74c98e7f609d05bed42343c62e79ae3d04f32eb", + "public/assets/img/svg/gitea-chef.svg": "sha256:985896a58f68b12226401a4e06729cc9e0ffa97269fc2e08b9e8f7c766894992", + "public/assets/img/svg/gitea-codebase.svg": "sha256:f84d816104ab245d9c8865b6471553afe166d1124f36e140f9c1272267ee1f09", + "public/assets/img/svg/gitea-composer.svg": "sha256:4f503b9442a15dd3f39b8b0dae3e5f64bc7857200420489d82f615912d1c83a1", + "public/assets/img/svg/gitea-conan.svg": "sha256:63d480977c7a2d1666de23f1e2d4d74ba3e5b4a02d6914ed27705ee27fa00d08", + "public/assets/img/svg/gitea-conda.svg": "sha256:be13399df9893362fb42e0f53125cfddccc314237ec8e409e035346fd82c24f5", + "public/assets/img/svg/gitea-cran.svg": "sha256:48cb44b285465dcd3844dd70fca6f2cfba39cc0063ebb6da79b7bfd84671569c", + "public/assets/img/svg/gitea-debian.svg": "sha256:180fd2e7024a24f1d1101b3aa6a068f4e3b2c8366760fdb4b6d2e724860957c0", + "public/assets/img/svg/gitea-discord.svg": "sha256:29f9f9488ae55d245f1a0be6db78318e424a0bfbe9452abf370fdc611a06f9ae", + "public/assets/img/svg/gitea-double-chevron-left.svg": "sha256:fea54153e3ace0b78969814f23428747026d6c5353155fd6138852adc9bff12b", + "public/assets/img/svg/gitea-double-chevron-right.svg": "sha256:be1686d0048c0f2d134952109a4ab17439c6c3f37519759d133ce2b418ceaaa0", + "public/assets/img/svg/gitea-dropbox.svg": "sha256:2d78d14e3c332288bc34095dce919baf34bedbb4a2f287c17544129c7f31e9a6", + "public/assets/img/svg/gitea-empty-checkbox.svg": "sha256:f6b797c096a27df5d35b61b6430f3ff85224be6efa78a2c9cc2407857280e4b2", + "public/assets/img/svg/gitea-exclamation.svg": "sha256:0261c3220947d7a6ee294fc58d690c832dadd108286107486fdffec04e93ab5d", + "public/assets/img/svg/gitea-facebook.svg": "sha256:43f39f0cbfc5d637b67290f877b9726642582463bbb007f4fbc9fdc0c5c52bad", + "public/assets/img/svg/gitea-forgejo.svg": "sha256:68a3cd179ccdc04ba3fea867a37b86158b10cafa0a19c4c4e87d9d9ca04ecb76", + "public/assets/img/svg/gitea-git.svg": "sha256:55df66b9203d0b6f77e1e6fb472cc5f814f56217bc9fedff4be161194ad224f6", + "public/assets/img/svg/gitea-gitbucket.svg": "sha256:b8b09fc8e6acb06f805ff42bf05f94082f0756d11b32169efeba34d7b54cc82d", + "public/assets/img/svg/gitea-gitea.svg": "sha256:fd9d2d2b368af2b750933ba4e6a187ba2022dbff4ae00564ebf4998d2cc061de", + "public/assets/img/svg/gitea-gitlab.svg": "sha256:baa9ee307c2a0aa5d4dc6213411c821fbff149433ac3e2f1b4bd0250655c157e", + "public/assets/img/svg/gitea-go.svg": "sha256:319eb659d7c08b382aa9d1008f7ac59ecec7b01b943ac26e6c7f816bf3c81715", + "public/assets/img/svg/gitea-gogs.svg": "sha256:e56480c0694a9f4d1c40c28a6d6605c1643e7ae94f9c1772f709105d8c13b2a7", + "public/assets/img/svg/gitea-google.svg": "sha256:11cb8bcb94924647bb3101baad919265347c775f897691e3ebc312da393bb674", + "public/assets/img/svg/gitea-helm.svg": "sha256:00de444ac90ee8ff70368fdd187118f66c20ec7c9463b80c63d0acd42c3c9500", + "public/assets/img/svg/gitea-jetbrains.svg": "sha256:c665c366f2f5a19ecb73fcc55511e41e218bc957325bcbcbb01879e755cabf31", + "public/assets/img/svg/gitea-join.svg": "sha256:83b85d70efd5ba5648ebae8de4d80d3fa6e8775a02706e97b4704f43a344bb4e", + "public/assets/img/svg/gitea-lock-cog.svg": "sha256:9b5c6312b1add218bc037682c6264cf03448ff259a3f0d4d6ed2f94f5035534f", + "public/assets/img/svg/gitea-lock.svg": "sha256:078a028c530f1c1b18c871affcbf1d3f7277cb17d42b442811752a9115a04c62", + "public/assets/img/svg/gitea-mastodon.svg": "sha256:9ea96456e84c4b7b40561699e41ec98ea038103e710265d034b503360c48bc82", + "public/assets/img/svg/gitea-matrix.svg": "sha256:0d0f88afc6228c502b40efcc08150be7753cac44889ca8a6e0b766be28d0d3ec", + "public/assets/img/svg/gitea-maven.svg": "sha256:d3c094106972bdc4d4342c444bfdb38ad350e5d0c3f84a07c8a29cda9ab80074", + "public/assets/img/svg/gitea-microsoftonline.svg": "sha256:19e6eede9f932cc8229b1ded59bf173fb633528a12c01510fe153828d9a41b5f", + "public/assets/img/svg/gitea-nextcloud.svg": "sha256:ea01e8a0ba122b9a54756d7bdee58b8e1a57f93d63caf3f5496850a32ec79bb1", + "public/assets/img/svg/gitea-npm.svg": "sha256:b93fa878e86a37a8662db553dd225dbd5cd6cdf047cab633bf4d0b3659050b1a", + "public/assets/img/svg/gitea-nuget.svg": "sha256:94ce2745c08651aaf4dafb005e746b4c9e15258aa2ce804698bdef670433147a", + "public/assets/img/svg/gitea-onedev.svg": "sha256:9f7d817d9a23dd3f919069067d73597577ab63e38aea811f366277008dd53fc5", + "public/assets/img/svg/gitea-open-with-jetbrains.svg": "sha256:1e9fac707400432b609e383d0561f5bbb5cc454c71567943559a583b14f1e9fb", + "public/assets/img/svg/gitea-open-with-vscode.svg": "sha256:c1df34f7fdc4a14a24e34e095f2637975e77a85b9f5535a95f5f9ef58f58cb58", + "public/assets/img/svg/gitea-open-with-vscodium.svg": "sha256:c74c54194011faa8f86ad2bb2ffe2b0e6fddc3e2aed92b4af88b15ef907d3d19", + "public/assets/img/svg/gitea-openid.svg": "sha256:d2415a4588e70dfcce3d96ab84bd94db869d3fda83741fb955c6952b17b872d2", + "public/assets/img/svg/gitea-pub.svg": "sha256:15cd58388cb0b2135afd5a41bcc25b5c3775f6a7f95869a7edea25a12ee2b56f", + "public/assets/img/svg/gitea-python.svg": "sha256:fb2b7909f20ef6fffecb3b3168e8e64d8e98e0f25feb44c7aa7933f738cdaded", + "public/assets/img/svg/gitea-rpm.svg": "sha256:f1c0c820f171e823d9ea456601e6c366afead74d030b7d4fa618c846a54a0bca", + "public/assets/img/svg/gitea-rubygems.svg": "sha256:6b1a6a988942cfe76ccd6e62946c52c31149cfb6e4b543c43e263b3d68596348", + "public/assets/img/svg/gitea-split.svg": "sha256:b9e183b51b10f7091dad5a629beace5bfdc9df9e33f587a74bd316b7cd740f13", + "public/assets/img/svg/gitea-swift.svg": "sha256:3f7eaf5fcb6b517bbf30377f885d72be1dbacd4b0c8ef664b43a72891aaf18cb", + "public/assets/img/svg/gitea-twitter.svg": "sha256:93391b93019fba4a8861c431dbd257d6b5dd6415e35b240725b0b7e587ef661a", + "public/assets/img/svg/gitea-unlock.svg": "sha256:67823de832bd937d6f3cba23960e6b0b98d929539b460053d834c360e292d6e4", + "public/assets/img/svg/gitea-vagrant.svg": "sha256:052a282ffad130306a33bc232769a3da6361a97a506fb7c85b900b20fe9bb193", + "public/assets/img/svg/gitea-vscodium.svg": "sha256:9edfd9549073b22524cd7f879b28957ba66244faff6f5e6b73d5777c2d36144f", + "public/assets/img/svg/gitea-whitespace.svg": "sha256:c29a70dd718c70ffa26e7202063c825da83f99543b6c812332fab55b69667c2a", + "public/assets/img/svg/gitea-yandex.svg": "sha256:1c51033e7c51db52ccc80316e419c2b98e15a35c3c29acff4c186610f3fedd6f", + "public/assets/img/svg/material-invert-colors.svg": "sha256:0380d8fd793824dfb6b45aa2b4af5a7172877f021279a26b39b3b9a86ede481f", + "public/assets/img/svg/material-palette.svg": "sha256:a629799ec1bc5efbe9170d24497b2d971d88b861807fe1b9a1c34af679e32b5e", + "public/assets/img/svg/octicon-accessibility-inset.svg": "sha256:e6916e240e24d311c31142e303aed49af1ee0a0fbf6586ee954e26cfeed7e882", + "public/assets/img/svg/octicon-accessibility.svg": "sha256:01924f653ee7f0c63adda44559deb7b81b5881902785b1a847b5bfeef76d618f", + "public/assets/img/svg/octicon-ai-model.svg": "sha256:54a8ac7bda3752120666c097cdfe1bf0440d4a1ea007bcfa73f0807ebc21d852", + "public/assets/img/svg/octicon-alert-fill.svg": "sha256:9b856bed4dd9919891eb3ab9317c8e43a02e53b358dd1ed6d66fe1078d3488c6", + "public/assets/img/svg/octicon-alert.svg": "sha256:a68bf3e0c2ffd0e51881ce4d0e192d147e646a873519a35f9652616b2ee36078", + "public/assets/img/svg/octicon-apps.svg": "sha256:98ae8329a2a1c6a71358032fbc5e1af92fd4c0198ef5931f03762b9e5cf0d8cc", + "public/assets/img/svg/octicon-archive.svg": "sha256:94ab63325602fa2b63adc3c63f16d2f2dc3966af4d4fb1d3f64ac3f19af610f6", + "public/assets/img/svg/octicon-arrow-both.svg": "sha256:37950f87f5dc176708ea6c899defe37d75191502c7a2a8263143d2e40f63e6a1", + "public/assets/img/svg/octicon-arrow-down-left.svg": "sha256:d7cb8ece70921188c12ec5c60cfd74e31cd6a0a5fcfa52c624e2b442e51936cd", + "public/assets/img/svg/octicon-arrow-down-right.svg": "sha256:0a66788ba6b71073575705b4c4b845f223ea87a103dc787e89bc92603ad6daa9", + "public/assets/img/svg/octicon-arrow-down.svg": "sha256:0ebac19b7d9145850e6c8e8033efedb0192c8db90aced9b5961c3ce75fe0b223", + "public/assets/img/svg/octicon-arrow-left.svg": "sha256:e672e623a62ac88d3d766c1d62e3a4d10046c5faf401b1c6616118bd863ac5ab", + "public/assets/img/svg/octicon-arrow-right.svg": "sha256:ab98e25c56e1b229500ca585325859bc4fa919085842a19bbb82909355141bf0", + "public/assets/img/svg/octicon-arrow-switch.svg": "sha256:56d93c5532915478f6bba47b9b5813c43941ecc3779b1d537533bca00322df01", + "public/assets/img/svg/octicon-arrow-up-left.svg": "sha256:1de2cace69a76d9386f3348bf2bc588ecf8e24f8e7bb3daadae7ad17d699a5df", + "public/assets/img/svg/octicon-arrow-up-right.svg": "sha256:f4893193c40104443b8b09faddbed6683be6e047ec9ed40e2a3b87c5f9c28379", + "public/assets/img/svg/octicon-arrow-up.svg": "sha256:f02ae00f16fd0ee5357bd7828411464a962ad456f741441e7eb32466a45278f6", + "public/assets/img/svg/octicon-beaker.svg": "sha256:6881d1fbb0977943150c73b8b9a6914f0e402ed1704fe2165acb31c2646f18d4", + "public/assets/img/svg/octicon-bell-fill.svg": "sha256:c01b1a251d41a615b46c5e43c1d1ddcb870832e28e8f8799b6cad39a18a278ec", + "public/assets/img/svg/octicon-bell-slash.svg": "sha256:d253c13f0c94718171c9329ab1e6e084948f3a2e7f7e7240f5fa2f5516cce282", + "public/assets/img/svg/octicon-bell.svg": "sha256:ad68dce1944d693bfcbb10631ef5c20fea1b9f0ae7c4212bcb3dc82d7da3e6f8", + "public/assets/img/svg/octicon-blocked.svg": "sha256:511be81a9a83b96e28a063f362b5798a94119b5fcde7ef772a030eb4d4d7ccf1", + "public/assets/img/svg/octicon-bold.svg": "sha256:9cc1bc158eafe1659503fc64b64d4ef06d98c56b789c9668fdfbd5828dd5e514", + "public/assets/img/svg/octicon-book.svg": "sha256:05bbf233f4492576b9e5d9bf185a47fc99316c39fb70dabfd81cfc8599ed52b0", + "public/assets/img/svg/octicon-bookmark-filled.svg": "sha256:ee50d6644125717471ab71fe03760cfe094d6373a26859a4f7e45615fe11c7a8", + "public/assets/img/svg/octicon-bookmark-slash-fill.svg": "sha256:b9c06caf5f2ceee3a24b922c2a8741231f6c731f4c1951c7537729a4004356ca", + "public/assets/img/svg/octicon-bookmark-slash.svg": "sha256:b23a8be18ad3be8dce7fd04f70332f2c1a4e3603ea9c004e6ec9e05c3e7c4a82", + "public/assets/img/svg/octicon-bookmark.svg": "sha256:34e7d0287602cfa205365dce3946e6b757f2005d5a01e03653787f2881ebb012", + "public/assets/img/svg/octicon-briefcase.svg": "sha256:5ca46a80d34c7942c7365d4f8b367b578d58f8c7a2f73b6bad840ba1d6c95b93", + "public/assets/img/svg/octicon-broadcast.svg": "sha256:02b6e159703e23a72b928454254df75eaa58832e252171afd85b5446b56135d6", + "public/assets/img/svg/octicon-browser.svg": "sha256:caa8262a2a62f1a69b87e2b05105c9a4cfd02354a1d77c29161b418c4783d5af", + "public/assets/img/svg/octicon-bug.svg": "sha256:76c3e0d4d254604b89fbdba0d78c8951295267d9a2b711500dad3e095a89abf6", + "public/assets/img/svg/octicon-cache.svg": "sha256:305bd5da18ceb9367f7da7d839231ef301cc6c65dc15cf831bea5d942e102a14", + "public/assets/img/svg/octicon-calendar.svg": "sha256:6080eec0c9f518994cd8a05977b4a3f5ba7d44141e7026ddc37de9567f3ab211", + "public/assets/img/svg/octicon-check-circle-fill.svg": "sha256:425fc914b7dfe14073cef224afd67bb10e3bb4db151ebcd2db0769c7a2e6d7cd", + "public/assets/img/svg/octicon-check-circle.svg": "sha256:209d5c6c434136aea17899f6e00c9367db71579d8e4c5e60a60bb437fe6d1500", + "public/assets/img/svg/octicon-check.svg": "sha256:3a795efca67f8ff815e3c65fb84e5a11b05a550c0194dfac0b658d4399a24b74", + "public/assets/img/svg/octicon-checkbox.svg": "sha256:e435092c82e68c4fb192eb12e08b19f8063347273d099b42883bee2806ee4c32", + "public/assets/img/svg/octicon-checklist.svg": "sha256:b3a3d78a17832547517d143266b2b255096b7ff958fab7df9300daac00b66110", + "public/assets/img/svg/octicon-chevron-down.svg": "sha256:7e92a73a44949e98d8267ddcda388a355b6627ab0694e799e30055399e18f547", + "public/assets/img/svg/octicon-chevron-left.svg": "sha256:e463832f7501ecbfe23440491af95f7daf745e72c487f49b41b5c89e8826a468", + "public/assets/img/svg/octicon-chevron-right.svg": "sha256:4b8d6a8e2742f5b4261b249c4437f7c6ab93850b4017648efbe4ec21b23f8b67", + "public/assets/img/svg/octicon-chevron-up.svg": "sha256:aa9c6ac56f1764c58f7a27b5f3d2213299d85960e10108b17aebc763a4b2fb57", + "public/assets/img/svg/octicon-circle-slash.svg": "sha256:ff7f12585b479acc59ce4297dbd1b239e362e8e143739d2f561b09b485f2cb5c", + "public/assets/img/svg/octicon-circle.svg": "sha256:8f8ca9e79a4ed84c2d6e30c0b498eb68dac0aa11de8921e96243a672e84b198a", + "public/assets/img/svg/octicon-clock-fill.svg": "sha256:7bd4c7a21ebae575835c46179029fd530d64a521baaa17897f21571e78d8ee94", + "public/assets/img/svg/octicon-clock.svg": "sha256:2f549310e5f2d582137d452292b5845a9d18c16df9b0f59527aa2bb3f75dca89", + "public/assets/img/svg/octicon-cloud-offline.svg": "sha256:ed942dd649e336e799a93b5a75be9d04b2a25d4f0af9998064f45acc8df5c182", + "public/assets/img/svg/octicon-cloud.svg": "sha256:f754d626091fa6ba81be85e1151f9379a372b9d50a65c6c4fceec385561cbf4d", + "public/assets/img/svg/octicon-code-of-conduct.svg": "sha256:f3c2992221c9789787504382632df06c830b7bd16d518f58f3e8fbb536d653c7", + "public/assets/img/svg/octicon-code-review.svg": "sha256:38d470476d7cc9dfad6356c6567920cb6c17585b61b89f8fee8521d986523653", + "public/assets/img/svg/octicon-code-square.svg": "sha256:ed25d2cbc226192bebf267d40357c426c11022821353706dae88fe03e212d98a", + "public/assets/img/svg/octicon-code.svg": "sha256:7dac4c68065e0d011e99a20a7bea6159e619a346bdcde0b91edaea8e57fb7623", + "public/assets/img/svg/octicon-codescan-checkmark.svg": "sha256:d0f6ab7b978972513b9f3dfc6ab989fbfbc8f0c20b3afef6f95ac912a943b906", + "public/assets/img/svg/octicon-codescan.svg": "sha256:db9b12321c114d1a87a1a32fdd9306ad68a96c2141c34b296d6f74aaede74b81", + "public/assets/img/svg/octicon-codespaces.svg": "sha256:9762d83c94fb6e0ce48a27cbc4deabb11fbc2c548bd59d7d58b86a78ffe953c9", + "public/assets/img/svg/octicon-columns.svg": "sha256:c08ced68626b34efcf7632de8f253cda709caadffba3d116aed34a0e86afadd6", + "public/assets/img/svg/octicon-command-palette.svg": "sha256:d0bfe402800d39aab77e13fb4031462ac3a0616934dc9b683df5a35588b5cbf3", + "public/assets/img/svg/octicon-comment-discussion.svg": "sha256:55b381eacbee97bcdada31504b67b71693ad39f2369ec47a7e3b1ae80e30c02b", + "public/assets/img/svg/octicon-comment.svg": "sha256:9c4e9fa03a049d0fac98eeb30e86153821321946f46caf40190a3d4f578ccb88", + "public/assets/img/svg/octicon-container.svg": "sha256:666fb77a3090d27d8239ad5e10088b2c1032bac2e7541ff5bd26bac45ea730cf", + "public/assets/img/svg/octicon-copilot-error.svg": "sha256:c50f8039e6ea72902acc250367a4400faf5315354c60e7ee1da1c9bc6a1c2d14", + "public/assets/img/svg/octicon-copilot-warning.svg": "sha256:1e159fe92b6f43b123bcabf106741b023261ed6b5c50da28d8713f895cd65eee", + "public/assets/img/svg/octicon-copilot.svg": "sha256:970cfc0efadcb19e2cae6720cb8beec3f5c2180e0cc68b4ab06bfd745a64d157", + "public/assets/img/svg/octicon-copy.svg": "sha256:48a0004c3f040618359b6af9b1f9b0e771782ed56e484ea8bf1fdb1dd3c0e0a6", + "public/assets/img/svg/octicon-cpu.svg": "sha256:b7ec6fdeba07acc0ecd1180348909bc254f84edef56d3a79d4ec9e56f78fa4ab", + "public/assets/img/svg/octicon-credit-card.svg": "sha256:9c2f94d60f9cfaa9d8c879043090239be79249b5e859bae8b0328432803b0c70", + "public/assets/img/svg/octicon-cross-reference.svg": "sha256:750615ae2b8fec2a28c6692bff5f8d5f2e838b690ac96d84bdddd038c044eedf", + "public/assets/img/svg/octicon-dash.svg": "sha256:c08d00de56e1f40ccfa9ab7f6ff62abe068980de0ac9fac525b7fad1e3a88293", + "public/assets/img/svg/octicon-database.svg": "sha256:c1e659c909489ccfa47c04f641216d12717efe143e152d327e9555372e5aeb39", + "public/assets/img/svg/octicon-dependabot.svg": "sha256:f2bb9aca70e63113ba7bd22fae996aafd3e7759cd6133ad215ebc9922b7e02a5", + "public/assets/img/svg/octicon-desktop-download.svg": "sha256:65a27aa0da1f2656d2160b3d993ec5f5f45a6212f6fccce4059218a6fb2bc2f8", + "public/assets/img/svg/octicon-device-camera-video.svg": "sha256:e22eb3a11ab5a25645be9213c37536e09c4670064fe6724a2d53db2df0f27274", + "public/assets/img/svg/octicon-device-camera.svg": "sha256:caed2d9682be9bf62249adba51553a63913ff4e23a0fa12cd968232ab144b151", + "public/assets/img/svg/octicon-device-desktop.svg": "sha256:e0e7aad491861b3c714d9fc81fd87ede314aec08c4f6a0fd832dadb8952b825a", + "public/assets/img/svg/octicon-device-mobile.svg": "sha256:fdd087c3c3281ca591e58d118c7187bf4eda7e829ab14aad2e76dd1553c4dffe", + "public/assets/img/svg/octicon-devices.svg": "sha256:b6395f67d7b09f0f9cd863365cab9ca21c484fc9950b0a6fefe438c0641d017e", + "public/assets/img/svg/octicon-diamond.svg": "sha256:8aaf49ee4bd556f5b037b700ccb3e7e54d9a82f63536504572aa917e55d15f3c", + "public/assets/img/svg/octicon-diff-added.svg": "sha256:19ba67ea86eb932d77ecf2201e68acc6c572e0889832e6a8bbf9878ca9a7cb24", + "public/assets/img/svg/octicon-diff-ignored.svg": "sha256:d43f9be2d38dd1d5b023e436daaf422c23dd330dab1eb307616d8f7c41a8fe7f", + "public/assets/img/svg/octicon-diff-modified.svg": "sha256:045dc0ee78d7cdcbeb94c4ba4c27a55c92e37b23cfb06fe8733bc2ed1f315ac6", + "public/assets/img/svg/octicon-diff-removed.svg": "sha256:87fcea2d793562d2f662fdb1a5765ed15f8ac9cf36a5eb267e288e10e121edc3", + "public/assets/img/svg/octicon-diff-renamed.svg": "sha256:0f4afd65d51086b8cde0f90c57f900d510e5a196248f9eb5e605ab4a1c2f5c59", + "public/assets/img/svg/octicon-diff.svg": "sha256:3022543550cc92398386547adfe60339c563f3ead1c0a51c18f2575da909dc20", + "public/assets/img/svg/octicon-discussion-closed.svg": "sha256:45e6bc927cac0d384eccefed9d3594915f21efa8627a65f728a6a8058f896f03", + "public/assets/img/svg/octicon-discussion-duplicate.svg": "sha256:9f16a33d0b72a26c67a7667b0eaf2e58e53b898e16c4538ceea4b035d1db3555", + "public/assets/img/svg/octicon-discussion-outdated.svg": "sha256:8e4223c08ae28a61709e0eefa67fe05c4de7dc1d0418a9e51d769e556bc94cab", + "public/assets/img/svg/octicon-dot-fill.svg": "sha256:05a2feccf6d481324ecb3b4bb1a948bc0b88271089537aef7be9ee903121252e", + "public/assets/img/svg/octicon-dot.svg": "sha256:76fc275ce79f3c64b2d656f2d6861682ce52ab73c06648b28557e523c8f4a890", + "public/assets/img/svg/octicon-download.svg": "sha256:1ac757aa85fc4eea50e9b2ad7d5b23d877e5141b5223081e45114f71330b06c3", + "public/assets/img/svg/octicon-duplicate.svg": "sha256:96c1eee84e20b614003e3e115b93bfd7b18bff0833220dc78a01d84349b6082e", + "public/assets/img/svg/octicon-ellipsis.svg": "sha256:62cf24eaeff66c18af8f1094e78b6172934937fd30924ef8e8b1c2288ef5238d", + "public/assets/img/svg/octicon-eye-closed.svg": "sha256:63373922bf115702776a889fb1f7faec889cf28c52320385c00e0f39840eb37f", + "public/assets/img/svg/octicon-eye.svg": "sha256:b615b31a72d1641c413093035e9c97d2f96a8fb2f99535447566ef37ab8e0ea4", + "public/assets/img/svg/octicon-feed-discussion.svg": "sha256:7b8697c35272059f950abf662e3f729a9d754465a026d0735eb056baf813b4aa", + "public/assets/img/svg/octicon-feed-forked.svg": "sha256:b610af02304e5044693aa7137eb75c0fca1d973ae97918a0f0cac43e0fde8a8f", + "public/assets/img/svg/octicon-feed-heart.svg": "sha256:9d5b5f7b99dd4fa84e9332fd1990e1e077fdf8540a18103dedc7a57772e5c24d", + "public/assets/img/svg/octicon-feed-issue-closed.svg": "sha256:3df86e5541631e4c440a147bb5b7677460f7dfdca90559f958ee2ead259f8f0d", + "public/assets/img/svg/octicon-feed-issue-draft.svg": "sha256:507d01d9afccc86d0eae7ab75205423c4f5cadc3325699b9c9ba9d076aac00cc", + "public/assets/img/svg/octicon-feed-issue-open.svg": "sha256:bac917eb064ff8476db4ccfd609171568b995fce7d6943d38ab563fa1065d142", + "public/assets/img/svg/octicon-feed-issue-reopen.svg": "sha256:fbf779a5c63b93a531202cb7c3cee98f0639d9050bf9cb31bd95e11a76cccbff", + "public/assets/img/svg/octicon-feed-merged.svg": "sha256:e50c6243d28e016eb64681c6facced47eafe5e27c8a7197d9128030a9b9b022f", + "public/assets/img/svg/octicon-feed-person.svg": "sha256:3a7ba6727b7ab2a275d7afcd5a555105d510cd8002fb54feebd8b8c403023c72", + "public/assets/img/svg/octicon-feed-plus.svg": "sha256:2ac37c38bf9fd787a0a18f2e1b35d5b7d153f0c98712906bc0e2335895f3c29d", + "public/assets/img/svg/octicon-feed-public.svg": "sha256:f82415ec9666600758e691f36474c614232a3d001f37c796809c787df4b19bbf", + "public/assets/img/svg/octicon-feed-pull-request-closed.svg": "sha256:d46016a1175e88b072b8b8854729578eb646ecd21b2ad140000f1dfdbf9e88cc", + "public/assets/img/svg/octicon-feed-pull-request-draft.svg": "sha256:ea6b875fa49833f0705392063a2e7fb7e836cb4d5147a3dc8cca98af982fbb54", + "public/assets/img/svg/octicon-feed-pull-request-open.svg": "sha256:0301b3c93acea29cab167e47a115fb7a265d9607c578cc22221f888436ce525b", + "public/assets/img/svg/octicon-feed-repo.svg": "sha256:2ee162199341bdf62a6227ffe70182bf3af5c65ddcd4daaca3554b094e8ef49f", + "public/assets/img/svg/octicon-feed-rocket.svg": "sha256:686cd4da1acec1c2b2ba37eff2d4acb44e70b0e4976805f57a1f8c3087791d65", + "public/assets/img/svg/octicon-feed-star.svg": "sha256:c4dd9cf833bb53f64d3fadd7bc178ce229402ff629fcf4ba765b2c513481c8c5", + "public/assets/img/svg/octicon-feed-tag.svg": "sha256:d86ce6c4ec506cace8539394fd53b5df51342c33be8ea987615130c00509204e", + "public/assets/img/svg/octicon-feed-trophy.svg": "sha256:f87efba73f201bf525463d706650628b6028d727509f20ab740f26f9f715161a", + "public/assets/img/svg/octicon-file-added.svg": "sha256:72db129141c9e138cb02d37d491e38b091fce3de746a10dc7ad988e9641b7356", + "public/assets/img/svg/octicon-file-badge.svg": "sha256:dc64034a04b192aef451fe6f5aadb8d5a848ca8c63df401c429b377af0e9637f", + "public/assets/img/svg/octicon-file-binary.svg": "sha256:438b5c3924aecce469c681a88c064e5187babf7b99cda266f8b79ae92217bfc2", + "public/assets/img/svg/octicon-file-code.svg": "sha256:d5694a42bffdc5494bb491a7aba2087946a56a2441ba84b0909e6363a573bd85", + "public/assets/img/svg/octicon-file-diff.svg": "sha256:8ca62b44e2a8398f3c5a29f247dcb9a2ef2be965c413e432dcdf94bf9507e2f7", + "public/assets/img/svg/octicon-file-directory-fill.svg": "sha256:dfb9312a55d5d8c2f723c2478e1147d334bc52d3e03d0a902f94583c3badc270", + "public/assets/img/svg/octicon-file-directory-open-fill.svg": "sha256:0838ee1e34c4a82d1b13c2b7501476294e91392bea9d4f741623ba9d0a1cd19c", + "public/assets/img/svg/octicon-file-directory-symlink.svg": "sha256:483e1d494641958499b1bf3e97a895b86280ca36025e6ba54255676bd19618ca", + "public/assets/img/svg/octicon-file-directory.svg": "sha256:6d32a38257d4f8279ba5b3da80c7a4555e1d8400d6757e1ec0d8580c8ef84dbb", + "public/assets/img/svg/octicon-file-media.svg": "sha256:4033351f204c83ad497ab24559ce0ac9dbc812035b260a25ff1da47483a916e1", + "public/assets/img/svg/octicon-file-moved.svg": "sha256:7c06ea2ae7f480017faf84dfced180874c433e3c47265569d42c520f7ce9cefc", + "public/assets/img/svg/octicon-file-removed.svg": "sha256:ccccd28f7170db6b6277dbccf9d46664c07c37fdc7233fa2ab748c712562ff8a", + "public/assets/img/svg/octicon-file-submodule.svg": "sha256:19a5f830ab49c41e151f69805319ac78d49c1e0ce6d58e38b3bcd1604f220869", + "public/assets/img/svg/octicon-file-symlink-file.svg": "sha256:df93f7f1bd2125bbec50c9116097f150a7120a36bdfaca00e3a753d1e19f87d3", + "public/assets/img/svg/octicon-file-zip.svg": "sha256:39e09ab4d4440c4e3388b19ba87a020d66b871f6726e87096f565ef46f0ecad4", + "public/assets/img/svg/octicon-file.svg": "sha256:c4906944c39c071f25112cb83aada709afea6e540cefe2a830af69043f66bcdb", + "public/assets/img/svg/octicon-filter-remove.svg": "sha256:ad84456d62c4bc098e4511ce450a4ded8294deaea085975dcb3d582f478b46ba", + "public/assets/img/svg/octicon-filter.svg": "sha256:fbefd495d292b8a04635b82e26544fe3f3db2852b43513c8937c143ab04acbc9", + "public/assets/img/svg/octicon-fiscal-host.svg": "sha256:f849adbe940af778ca1f13bcc51d6779b2aeae4dba5d579f1a92f7feebec226f", + "public/assets/img/svg/octicon-flame.svg": "sha256:ec5fb7f018dfe1f3b8b5b1d5e45bc9a6f7ac555e3d83cf5cd2eb88ea1dbbc28e", + "public/assets/img/svg/octicon-fold-down.svg": "sha256:589d09f2dfee82a4dbdb5d2d268054c786a91d645fc1bb8815d387538230d882", + "public/assets/img/svg/octicon-fold-up.svg": "sha256:a6b90c141067e1c50f9d61c6ccce0c481cedc98fb081ccde0b1d3064a94723b1", + "public/assets/img/svg/octicon-fold.svg": "sha256:e3a76dceabf08c9c493df372174780ed8f2367f5ec406a3156e07f0f32051727", + "public/assets/img/svg/octicon-gear.svg": "sha256:b874521049df21c9c8c57b8422e72f6f47b274a93f54052f0ea24ce6f983b77f", + "public/assets/img/svg/octicon-gift.svg": "sha256:3c7220d3a304984d90358b0f60e7e8afb49746a4abdcd7a25a734fb1bf96882a", + "public/assets/img/svg/octicon-git-branch.svg": "sha256:d7216df7b39661f6a91fdb77a77c5819b8afb3a6c8762b085139c4b10ff92f8b", + "public/assets/img/svg/octicon-git-commit.svg": "sha256:b89ca8ccabfe0a1828978a2602eb6a14e18a4c6bddadf99fe468a3006097a0e7", + "public/assets/img/svg/octicon-git-compare.svg": "sha256:be7173385863ac928bfde7c018e60e0dc2e0a1f280373d194dde453716ec9ed5", + "public/assets/img/svg/octicon-git-merge-queue.svg": "sha256:08dcb24210949b6e7469456198840b72de9a907eb6c5a8f8bbec791e4c23851a", + "public/assets/img/svg/octicon-git-merge.svg": "sha256:7232cfc16b3170acf2b6823eaf06d4833211853fefc2b71ba967a566491e668a", + "public/assets/img/svg/octicon-git-pull-request-closed.svg": "sha256:afe5f67867337014bd43ed9f41501c57312f0b4669f78138c5edc6fbff65ec99", + "public/assets/img/svg/octicon-git-pull-request-draft.svg": "sha256:263a8f001f512dbfe120c3b6ce1858edec543891269ecafdd0b223ea8ab897cb", + "public/assets/img/svg/octicon-git-pull-request.svg": "sha256:d9a610ef9da96cb6928f8cc4966979413c771744968de1fbaec26b125284dfae", + "public/assets/img/svg/octicon-globe.svg": "sha256:b9a14955588412bfc2753def149cc685a5ce6ea37af0f1f6f468b3ab8348bf3c", + "public/assets/img/svg/octicon-goal.svg": "sha256:988885a59d124b873d9bb6c56c5fc9d41a34c8c10b52931a3d9b704605ee8947", + "public/assets/img/svg/octicon-grabber.svg": "sha256:75a476ddf454fc0cc2739728db3cf236fe03b251a5df4f6da3bda43f8643b78c", + "public/assets/img/svg/octicon-graph.svg": "sha256:d532f046e4eee468b5c07e0be162ba7d556693b8d0a30bd85025b7abcff7b4bc", + "public/assets/img/svg/octicon-hash.svg": "sha256:e794c593529a348acb434c89237512cc63b967425be6f6c110fe3813fe5947b0", + "public/assets/img/svg/octicon-heading.svg": "sha256:ba329d7c1e19cb2a90b289119413a4b9c4b5d9fbc1a45a10a8e929b75090347e", + "public/assets/img/svg/octicon-heart-fill.svg": "sha256:3bc1bbac480a41e09c75c61b33cb5600a998e854a5dee31807805ea964c1bebe", + "public/assets/img/svg/octicon-heart.svg": "sha256:fbf26f9e7c2a1e30a12a1327a2e6a6164df3144712008b1f4c4c6258090f6a41", + "public/assets/img/svg/octicon-history.svg": "sha256:48d8fcae5280a2b416e069832fb49d9fe53e5a24fc2b4ec8e3356b3fd08f0b2f", + "public/assets/img/svg/octicon-home-fill.svg": "sha256:74d55ef033a1252430c28513a6a7402dc53ced4cf14a17410cbd3735bbcee017", + "public/assets/img/svg/octicon-home.svg": "sha256:e68a7b8d2f7f03094fc992ff47eee81ce1916ff0c081b1de000aba9e1e2c9998", + "public/assets/img/svg/octicon-horizontal-rule.svg": "sha256:683756ce5faf7b6a4d24385f05bc4f9392c524258c683f71d403d024c7743ebd", + "public/assets/img/svg/octicon-hourglass.svg": "sha256:2ab9abe0cf7d0b0f7233db6072adf55e1748700d6cf368670362076b3e734e95", + "public/assets/img/svg/octicon-hubot.svg": "sha256:a77b170e5faefa885a160c3a87bfec71d0c64fc718c8a63b3f7931c0d347ef8f", + "public/assets/img/svg/octicon-id-badge.svg": "sha256:0fd199bcc1a07ed5584d3574e9bd18f28d17aa8084e1c55104d5b11485c1fcf1", + "public/assets/img/svg/octicon-image.svg": "sha256:92296d48c345788b7c09a41b244b1089524108d6fc65c05b87dd17c054040678", + "public/assets/img/svg/octicon-inbox.svg": "sha256:19e85a974e6e519ba542675aa8fc61643d90e2abd3220aade6d70544adfd5c3e", + "public/assets/img/svg/octicon-infinity.svg": "sha256:7dab4dc7ea127b0fb8218ec912edcc9c2394d4930682f12a366d2f5dabd06ecf", + "public/assets/img/svg/octicon-info.svg": "sha256:7970a86012e0c08d0374875c09531b41d692a35cfaa52b3e6efa2a403b69f1db", + "public/assets/img/svg/octicon-issue-closed.svg": "sha256:0d2899c1a97cf633bfcf5164d62b0037a7fefa4c385da4fecb8f08cbb7de6b92", + "public/assets/img/svg/octicon-issue-draft.svg": "sha256:27362ce0def5e32c86449b6a4e4c92b3c462b4801478a90fc37cc4f3aa11136a", + "public/assets/img/svg/octicon-issue-opened.svg": "sha256:144a79d3d2d5a043ee8631d8a1ab25500bcbd4f0e41f2122b6a5931b2e87b7a3", + "public/assets/img/svg/octicon-issue-reopened.svg": "sha256:bc1370b36d2bd622ff4cc3a3c5deb9377cef47f64b17941c560ca27270f2b449", + "public/assets/img/svg/octicon-issue-tracked-by.svg": "sha256:a9976f2648a896d984c5abb587d32fec32c4f2a933675738344d5b432632e879", + "public/assets/img/svg/octicon-issue-tracks.svg": "sha256:604efdc4d405801af57078dcf952f08373228a652618c90bc5dd32105c99e4c0", + "public/assets/img/svg/octicon-italic.svg": "sha256:59a7c817c5ba75ad6850dcea8b4f68500cb0ca655e7b48b9f1e185f08da8cfff", + "public/assets/img/svg/octicon-iterations.svg": "sha256:f5fa311b1a7691994ffa20d7c189d71d84e36127fb86aea28703fab05b87d560", + "public/assets/img/svg/octicon-kebab-horizontal.svg": "sha256:ed4b29dfcf2117624edfeeeb457683b6475f1920a619265d5ab6ef2b4c4b5eed", + "public/assets/img/svg/octicon-key-asterisk.svg": "sha256:66018fad70b363aaf8bc3142f93ef18eb9fa288cfba23e74e008f8a68eb121a5", + "public/assets/img/svg/octicon-key.svg": "sha256:5c3fd71a68af735b6dc431c818ee153344dd4dc68c0560975ff771f1c38a42f3", + "public/assets/img/svg/octicon-law.svg": "sha256:da12cd47cc684709556fa23c76af0901377d58afd841417ecbe562ed4bc04b2b", + "public/assets/img/svg/octicon-light-bulb.svg": "sha256:1e47d9dafa8df2cb01549d6901ee8afb5c2723100987de635dee73020b305c9d", + "public/assets/img/svg/octicon-link-external.svg": "sha256:a412ae67974268c9e191bae198ceedba74505f8c77603286c9060804a22fe94d", + "public/assets/img/svg/octicon-link.svg": "sha256:941ffd51548d6b1dac0895b025ed31886310367439875966e14c478793e4a804", + "public/assets/img/svg/octicon-list-ordered.svg": "sha256:8a9f1c6ced3d7d943a6821a6ecfe7db46c3bfd84e9d8ee767b0fc7d6a711f45f", + "public/assets/img/svg/octicon-list-unordered.svg": "sha256:252ec58197412bb47e224b3b9e3a43946fd564fa26dc8b986e54ffef4db7fde8", + "public/assets/img/svg/octicon-location.svg": "sha256:e1e4b5bf55ed7fb3a1f20ca07b342105c8cd0abfb45f03fcc6e0d742fa1ac8e7", + "public/assets/img/svg/octicon-lock.svg": "sha256:ddb45b7ffc58155a8a51f9c6c5be46b22c19b83eb51e43170e0f56f0b8547546", + "public/assets/img/svg/octicon-log.svg": "sha256:10ed3ba67ee6487e74d292c4db373377b9634770979734d6940bbbd34704254a", + "public/assets/img/svg/octicon-logo-gist.svg": "sha256:21711dfd79627cdec9b2e8306c05e584eb16e12ddaac22b049b4cc9db349c00a", + "public/assets/img/svg/octicon-logo-github.svg": "sha256:b5fc217ccd6c9450e61a175fcae67f74c5437e982841506793fd9e7ea26e1673", + "public/assets/img/svg/octicon-mail.svg": "sha256:de5567d3b7c33e8dd1e73678c67c056b91cf139d766f26639ac841ed35e82034", + "public/assets/img/svg/octicon-mark-github.svg": "sha256:962fe89e9af434da55a6ceaf924c259482dfec34c75809b3ff4e70cb8feb57af", + "public/assets/img/svg/octicon-markdown.svg": "sha256:6c2c2f2958b135321ed6178105908120541d934ee97c8192e0c7813b18aa0aaf", + "public/assets/img/svg/octicon-megaphone.svg": "sha256:a08512515fe0bd255221d2323bd3e9a06f6298cda0a02ac7c340b6b8e64cae98", + "public/assets/img/svg/octicon-mention.svg": "sha256:fc0d11dcc0d386dbab2d040bb764a6c8734de505404a211db4909be7c532966f", + "public/assets/img/svg/octicon-meter.svg": "sha256:76026bf3de9017a02d3927a5bdfa5750c635bb716eaa1dad2b02f11b93a15480", + "public/assets/img/svg/octicon-milestone.svg": "sha256:9e3c19287002f6230d90cbf87c4016ea516b5300be9829b54f760d81a6450686", + "public/assets/img/svg/octicon-mirror.svg": "sha256:b4f2d3734fdb183549fa4bd23b45a6e8449e1b596cb25e3e45f1b1f08f293fe9", + "public/assets/img/svg/octicon-moon.svg": "sha256:9edca588b5c668df7429f82f9b97c50010924a68384d7d9ee077375dedd894e2", + "public/assets/img/svg/octicon-mortar-board.svg": "sha256:369ed8f0aae201aae33df97a9f27d1f3c0b5607d3fc278d9cc80737aa1607cb1", + "public/assets/img/svg/octicon-move-to-bottom.svg": "sha256:95548ce043937a518e43681dd39ee9c98a8b30e07ef79b6474507fb3f30fd448", + "public/assets/img/svg/octicon-move-to-end.svg": "sha256:8f2831e60bcc492d1d921ade22a4a32df6c48e2628945b051d56ae094cc697a6", + "public/assets/img/svg/octicon-move-to-start.svg": "sha256:00d57f250b3be9cb275140a27184afe31d8f7760c7edcf4daa9851dca3fa5452", + "public/assets/img/svg/octicon-move-to-top.svg": "sha256:d2e8aacfbd358d603b20267b1cc109a12c03d93d639f55da20836396ca5e0efe", + "public/assets/img/svg/octicon-multi-select.svg": "sha256:faa1475b35889e590bfc8f888d5cab242c9889bbf8c528f2129114c3529dda46", + "public/assets/img/svg/octicon-mute.svg": "sha256:e5aa551c7d94f0235a94595a8e6751b3c8e049affd4cc7b6f3a75c5cb0d9e834", + "public/assets/img/svg/octicon-no-entry.svg": "sha256:754177e8511a84dd10b2f88b91ba1707d4b4436321f0ff683aee7e0ab540a656", + "public/assets/img/svg/octicon-north-star.svg": "sha256:6327d396011cc7badd1e9a9b9f276b83a382e35310a87ba4257f41aaf79d02a1", + "public/assets/img/svg/octicon-note.svg": "sha256:c82f2825342f02c2ebe4628649c06fcb190da92383dce2ead2f7a1ffda6b18a2", + "public/assets/img/svg/octicon-number.svg": "sha256:0807908b2921cd1d2d6cdf252391ed9877761c33340826dd09063d41cf85189c", + "public/assets/img/svg/octicon-organization.svg": "sha256:e14121bacbbe7fb7a1771c73dbeb1150db3738ca07b2e4344d72467aa36cc738", + "public/assets/img/svg/octicon-package-dependencies.svg": "sha256:17f612384a90068497431a8cbd05ea100dd01799fcf765de50c8e66891ee9c1a", + "public/assets/img/svg/octicon-package-dependents.svg": "sha256:5d7599ee90a6803d0744d4e2dbaa8718c3f3319aded162e84cc751e2260d9393", + "public/assets/img/svg/octicon-package.svg": "sha256:de34bbe77ac55b8489033fcc4a9a0912c1dcf076c5babeccdfc995329e86c478", + "public/assets/img/svg/octicon-paintbrush.svg": "sha256:bbdfc004128a0fda29e85799737e572eef2258b5eb9d47ba8dde0ff77ed42d24", + "public/assets/img/svg/octicon-paper-airplane.svg": "sha256:29999226cb67403928d9b711737752e0f7b71a51fb3ea78afb38c49238590fd8", + "public/assets/img/svg/octicon-paperclip.svg": "sha256:5cabe6a9554bdc59392f1a58107881f2066439283dbcfb6aa2dccc7c7c41656c", + "public/assets/img/svg/octicon-passkey-fill.svg": "sha256:0ad22f9cb20997d5563f80555239cddeecee97c82b44643fd9b67ecce5ce1bf4", + "public/assets/img/svg/octicon-paste.svg": "sha256:9e2b3e17f4a7e68c2ab48d89d62ffde7c6dd6b0c0bddd7bdba3dedc3a5bfcc19", + "public/assets/img/svg/octicon-pencil.svg": "sha256:5f86be065332074f19a0eb8be214f2228a79f9b62f9eb3659683a554500497ee", + "public/assets/img/svg/octicon-people.svg": "sha256:9f2d0ea045deceac89833b4ffd32c0b8fbcc0f080df93f42f1f56d09532d1bce", + "public/assets/img/svg/octicon-person-add.svg": "sha256:87d7a2cab1bfdcd164b47c1db3bd0ff8180fbacf61ecca8bbbb369c0f5da5d4c", + "public/assets/img/svg/octicon-person-fill.svg": "sha256:7c945476f1194939130932ca2512303f30acd61edcefe168523174c8057d8f00", + "public/assets/img/svg/octicon-person.svg": "sha256:a2b7c10b330893f426925ed9cd9a9c8a70d75af706bcaaa8291c15e1380626eb", + "public/assets/img/svg/octicon-pin-slash.svg": "sha256:2e5a54fe5826073078defce032893a0ba39ae19f40244877c8d8909882fca179", + "public/assets/img/svg/octicon-pin.svg": "sha256:647ba993a8c8dddf7e6db79d87032c3213f87fccdd556832510b4031b4834f42", + "public/assets/img/svg/octicon-pivot-column.svg": "sha256:82c9562782a0082620f73f5d2f7a2c6a18d9fe301b349a54afd1fd0fe031ecbe", + "public/assets/img/svg/octicon-play.svg": "sha256:95b7523e2095ab4a77f74fd458e2c32d4c20c3572f6a302c16f01f9be0409d0b", + "public/assets/img/svg/octicon-plug.svg": "sha256:cf07958d2c28dfda7d47c9ba9a64a5e8e8f47e9cd2ad17cf84609c4630fcf7d5", + "public/assets/img/svg/octicon-plus-circle.svg": "sha256:bc34509177e1b7e18a3132c24db5c00038d96d2e10880b029d77c23c58ece176", + "public/assets/img/svg/octicon-plus.svg": "sha256:074e0dba400097c61798e85bd2934f91efa91ce0a78e8f8972c41f8c0f3ded3e", + "public/assets/img/svg/octicon-project-roadmap.svg": "sha256:73476d8223136207c6269836a9c913bb268cd694c5279fc7fb47004b0898ed23", + "public/assets/img/svg/octicon-project-symlink.svg": "sha256:9c352a7c3ad3cf555692f281199fcb94ae9f71e1526fcb31444626ec0c1cb024", + "public/assets/img/svg/octicon-project-template.svg": "sha256:0d3ba64f751bd3ac01b0fa2a6fdce5edfa0888ad63109ea39ced1a37a1958572", + "public/assets/img/svg/octicon-project.svg": "sha256:fdfb4964fc5cad4545d5df9a3d99600a8041f3866885b7db4fcc65537dcb4b88", + "public/assets/img/svg/octicon-pulse.svg": "sha256:d935d6ea670cf4f108bc4be046648b2d47e16e561c61c7160c71c7cfa9d83f6e", + "public/assets/img/svg/octicon-question.svg": "sha256:faa6c292a1a7b17c6402ac50d7aa055805badd7e60c9874480ebb2bf1acd496d", + "public/assets/img/svg/octicon-quote.svg": "sha256:0c2ee762c766b3b42963fa5da5714b963475b72bebfb22b71642bb0b9adf945a", + "public/assets/img/svg/octicon-read.svg": "sha256:9ee9cd3ce35952e84895eedfc9fb39a7b71d486cce0f5a965064f7800d4cec9f", + "public/assets/img/svg/octicon-redo.svg": "sha256:8ee526dcda240e28dedf7ebbb0f8d1b170f4f0f5856026bcbb59a3c364031875", + "public/assets/img/svg/octicon-rel-file-path.svg": "sha256:b73f47516e9dbe5cd26728f3238bbf98bfa13c24560a9d2ab826380c54910817", + "public/assets/img/svg/octicon-reply.svg": "sha256:a5dc052d0db2b81576d4edc169bc3704d1553281161257933fa86fa72c59b822", + "public/assets/img/svg/octicon-repo-clone.svg": "sha256:750f3a4d8b4441146291810cb2dc510afc6c2f4904198f5566d5f41c1980719b", + "public/assets/img/svg/octicon-repo-deleted.svg": "sha256:62fba8a2a9240d3c263a33f302f5a4e8fca02237a680e9ef4cafbc51c19c5f4d", + "public/assets/img/svg/octicon-repo-forked.svg": "sha256:d54d3c1e121eb4c790fb40e444f4f30e8be60281fce313302adeab080de9e459", + "public/assets/img/svg/octicon-repo-locked.svg": "sha256:0809fa493ff42439e6fedf0eb1b74766e42f50152d5ee10536bbe89e9d1b565d", + "public/assets/img/svg/octicon-repo-pull.svg": "sha256:db96e9a42b27c1f5b9868d23770b9470b9a2e7fc3506d18af1c8cd3fbcb81522", + "public/assets/img/svg/octicon-repo-push.svg": "sha256:13f77b6c0e50c37d5640d006f3fca3f0ba516c250258ef14fa42cbfe2d1ad0f0", + "public/assets/img/svg/octicon-repo-template.svg": "sha256:8c979c85b218f8c851cb4adceedf53990a536b8dcf2854360d55d9a352056d2c", + "public/assets/img/svg/octicon-repo.svg": "sha256:1a0e6759474e7dacf250c7824798167af69fc2e4f39890a25359810a23af406a", + "public/assets/img/svg/octicon-report.svg": "sha256:779d4c2e7dd6fe119cc6ebc86ac383bdb34945a7ea475b1224c15dd8b3c64d5f", + "public/assets/img/svg/octicon-rocket.svg": "sha256:d685a37c8a9ab23d92e09e9e0feb170d964a05e6de882294301c055b2f318517", + "public/assets/img/svg/octicon-rows.svg": "sha256:a07cb91aee79e87443255698d517256e0d6b0d47b86d7843d95e00ca0cad2fbf", + "public/assets/img/svg/octicon-rss.svg": "sha256:30cda6c55098a25b764f243680c1a962615cd94c7671395f21e6a10ffacf1611", + "public/assets/img/svg/octicon-ruby.svg": "sha256:c1ec26fe5f7ba264e63c4bef88e4d65c829f8d93c43d2f4928291bec700abfab", + "public/assets/img/svg/octicon-screen-full.svg": "sha256:f47104e716806afa672fa955da69d7f48f6c5cbf67e7571a88a6e2363c65d1f5", + "public/assets/img/svg/octicon-screen-normal.svg": "sha256:b4ff3d4a4e60b9f5e0e41579154c4d54dda45124784431341261c9d84e963db6", + "public/assets/img/svg/octicon-search.svg": "sha256:6afea540bfd9e961a6969cc6861021532e3e399ed3f4fe49bd1701298d6611d4", + "public/assets/img/svg/octicon-server.svg": "sha256:7bdd3e026f99a055ac3670374b3491a20d40e6496dd4f90f7f6f80ae39d29145", + "public/assets/img/svg/octicon-share-android.svg": "sha256:fbc66172b809762f85e77b97823ffd371029fcb804989b6e5ac4e684f1cd0b0f", + "public/assets/img/svg/octicon-share.svg": "sha256:25e9fbcaa43f09982725bdfbaa79b13ee68766814b82d8d33ab8c98a59381572", + "public/assets/img/svg/octicon-shield-check.svg": "sha256:6d9b2e0dde94c271af8842602cd6f218713dd7454a8d78b3bb063909f04178e0", + "public/assets/img/svg/octicon-shield-lock.svg": "sha256:595f0016516c096138466559573a22ef96c8b862e5d748506b983d7447d8d4fd", + "public/assets/img/svg/octicon-shield-slash.svg": "sha256:60290dad8cc3c330572c11b14abca08a00a027ea2c57f96f55f579412ac9d0da", + "public/assets/img/svg/octicon-shield-x.svg": "sha256:f8242ff0619e85c6d2cdd30221821e33197d3cadcba4b18f98cfd391836c6c9c", + "public/assets/img/svg/octicon-shield.svg": "sha256:1b9ee11d9419667f4e50913477630dfa288f96087330eed970f3100683f1fde2", + "public/assets/img/svg/octicon-sidebar-collapse.svg": "sha256:a032e748f221d8f03783b99c2da22299f46e059e3ddcbe969fe06b9987c1df8f", + "public/assets/img/svg/octicon-sidebar-expand.svg": "sha256:33fdc1f9bfa4e9a0470084370b74d3ce6a33be023cf878f4a528b62c7d9afb56", + "public/assets/img/svg/octicon-sign-in.svg": "sha256:34000706e11faf10a151a5a59d0937b4a387ea8aedbec347eee555ba6ddc5342", + "public/assets/img/svg/octicon-sign-out.svg": "sha256:f2549911e4ca8d0d6de75c82c9bf295818d97f774b3e905d22444b1f5fa623d7", + "public/assets/img/svg/octicon-single-select.svg": "sha256:a8eabeda6d01483dfb9ddad6ebc66e4e86ebc1fe658cc7cfe03dbf39fea43e4b", + "public/assets/img/svg/octicon-skip-fill.svg": "sha256:90832f2f9ba65f8b7272d43eafa1749b96fccf867b87ab807d52298c62d3c4a2", + "public/assets/img/svg/octicon-skip.svg": "sha256:3bfc4c8166aa844990382a729d2c4515c7f292734cf4dc178d41f9bafbf09e94", + "public/assets/img/svg/octicon-sliders.svg": "sha256:a8e3063ce2146051c2f94a43a0bb3a09051fd6f90c5cf7287d4b2e93fad17c05", + "public/assets/img/svg/octicon-smiley.svg": "sha256:cfd4f838e150e39b6e076e25daac352bdaf6e1ed5595d9e53b1061d3409a3440", + "public/assets/img/svg/octicon-sort-asc.svg": "sha256:15ce8cae4943746da7bf63894191d92007577422c36ef1439ba37ed59eb44e06", + "public/assets/img/svg/octicon-sort-desc.svg": "sha256:73c7b2c68cd9ee0e3d62d9e9edc85c51414adcec61ccefaa214c89652bc0fa7c", + "public/assets/img/svg/octicon-sparkle-fill.svg": "sha256:e896b034ee5596cab02202e8903e1b0774a62946b441e7d34db3c5457cad6bb3", + "public/assets/img/svg/octicon-sparkles-fill.svg": "sha256:8e74a5ec35a8064fe4173f59d6ffd4c81a5aec82bca6a741f63b018463435c5c", + "public/assets/img/svg/octicon-sponsor-tiers.svg": "sha256:39c98e95391ca229cee10a678863079ba4169ef3b491412c34fc3373785f5c9c", + "public/assets/img/svg/octicon-square-fill.svg": "sha256:85a8a2c6eb321784aa19f666d308932775a7b870924575f292fa3d16bd2c6f06", + "public/assets/img/svg/octicon-square.svg": "sha256:8f49b287e30062cbaa4c1837d12cd762c6d51d08debc67abd03c6600675a6534", + "public/assets/img/svg/octicon-squirrel.svg": "sha256:c57ebaeaeba301d3733e9cb0100cc0c2807e605130abf4ae4e70fb4781a59d37", + "public/assets/img/svg/octicon-stack.svg": "sha256:f1e4589e4e39249a28ff4fba2e61155784d49711527c1e3084b190d889c0c7b6", + "public/assets/img/svg/octicon-star-fill.svg": "sha256:622aba8b6ab0a357c68021b715b135b9f878f61d5df5e520ae0e02ecd34d6567", + "public/assets/img/svg/octicon-star.svg": "sha256:a0f000d5016d0b089dadfaa56c51f11c0598f9d960542f7e2a1ecbe3367cddf3", + "public/assets/img/svg/octicon-stop.svg": "sha256:1fc480cbe154b7d1c074b38fe22487cde6bb9c50bd79c1166df5eb67a04f4017", + "public/assets/img/svg/octicon-stopwatch.svg": "sha256:3e84795e13db4827e2b3408cb997d71a386bd5a6ef3177be88bfe0b97407423a", + "public/assets/img/svg/octicon-strikethrough.svg": "sha256:9803435b1b33b56dda41221564720417f636b0757160cb4f1166e5e70b048183", + "public/assets/img/svg/octicon-sun.svg": "sha256:52378e036b60a474cba614ce04f0c7db34e8beb92c1d9c501a55382d8d1b471f", + "public/assets/img/svg/octicon-sync.svg": "sha256:506e1067e3a1e19e3344edd71e7162c0e02c81c84f8604a7e9bde148bea77ee4", + "public/assets/img/svg/octicon-tab-external.svg": "sha256:e8bbe61f0a041e3e74e524ca2674cdcd36a687f02358ddcf3a139a3cfb36843b", + "public/assets/img/svg/octicon-tab.svg": "sha256:21ef1d785a80483a635e281d2e8470a2ec486352206c6edc28feee9d950c9c0a", + "public/assets/img/svg/octicon-table.svg": "sha256:c5cb314e265dc7ceb1f9e423c5ab20a11626d44950a469ceccb77b25887e4a38", + "public/assets/img/svg/octicon-tag.svg": "sha256:29605a67e1e79591018d14d8ee36346ad2f8a8d88da04f52d2485f2e0c569914", + "public/assets/img/svg/octicon-tasklist.svg": "sha256:a49dd306e838e23cf768d3a529c6f34370b7e0cb96d8b4cd050b669c92183a94", + "public/assets/img/svg/octicon-telescope-fill.svg": "sha256:bef8477788ded094159bcb298bad9aa7397c1118f70dabc206c420a6a0c3a3d5", + "public/assets/img/svg/octicon-telescope.svg": "sha256:daaa7679ab2417e646e5fc0c0f3db8a21d31529c44e5437495ee608e0703ac5d", + "public/assets/img/svg/octicon-terminal.svg": "sha256:b153e0bb6e50a3cd35d38a2d7b81785833861329f83f52aba53d84385d003a3d", + "public/assets/img/svg/octicon-three-bars.svg": "sha256:5bf90c265a8131ba9f0a82ab5523c73dc4528caf9066a16f78f94209ee23d4cd", + "public/assets/img/svg/octicon-thumbsdown.svg": "sha256:6828daff26639617e3661ea02b7a3d0f74e092416c447f668dec6d33847ae48c", + "public/assets/img/svg/octicon-thumbsup.svg": "sha256:120ed1eb239ca58316787bd73da2db1031ea3fed50025d9c9479a2d60d7b7a12", + "public/assets/img/svg/octicon-tools.svg": "sha256:a8a63805385f127016942f54cfb8eb5a86d3e4c066cc67e0ffb34c06eb74e880", + "public/assets/img/svg/octicon-tracked-by-closed-completed.svg": "sha256:cfee9362854ab301375d95fab09c0bdd3877c73ed640729fd4f934a7bb4eb73b", + "public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg": "sha256:74fb613ad62ed1f28b464f7cb10b5e759e1eb0d38ffc60accc7c40badf27fa5b", + "public/assets/img/svg/octicon-trash.svg": "sha256:e7d00ec99a1eb980a9f7aaeb190dc7b97ef163161c64175d4d6cde3ccdfa05b4", + "public/assets/img/svg/octicon-triangle-down.svg": "sha256:7329b445017393f5b0867024c4d40676e502fcbcb7408380d434326bb17be127", + "public/assets/img/svg/octicon-triangle-left.svg": "sha256:d366bf94c38ec830e7b97ec3bb1fdb8727df0bd93616cd29651051d3ea494537", + "public/assets/img/svg/octicon-triangle-right.svg": "sha256:ece653b587bc6624a480574e0263d1b03837596b9fa264dafd2a9537c24f8705", + "public/assets/img/svg/octicon-triangle-up.svg": "sha256:1b3938555fb3bd49b1b9c839b528b676954a0b00785836dba58660c3a366faab", + "public/assets/img/svg/octicon-trophy.svg": "sha256:7fda16ddfeacb0dfb6101535b6f97b48eb680792a9dce613e00e32f000707cc2", + "public/assets/img/svg/octicon-typography.svg": "sha256:3e430adcc27e23c78afee0183eecb8854d65de0704d43eddb9da3ecc790a7856", + "public/assets/img/svg/octicon-undo.svg": "sha256:b16a0d032096084026a8732f850f8072cf7e73ccc42a95bccb33402eac83e6b3", + "public/assets/img/svg/octicon-unfold.svg": "sha256:5e0a2d91104c990270e7202cac338d56e078fbed96e35b2dbaebcd11d80ed800", + "public/assets/img/svg/octicon-unlink.svg": "sha256:379400a8f8a612783b3934583f83e33a0c27f28b7a99cf44065f7ab98c329100", + "public/assets/img/svg/octicon-unlock.svg": "sha256:3aa78c076654c46d67ca93784b5b83245c439d25e318d2fa6f213a6a807b089e", + "public/assets/img/svg/octicon-unmute.svg": "sha256:2d2953bf68f556d0f263b80ca1d2a8037f0973fefdcdb3df2d349bc8e15ecd29", + "public/assets/img/svg/octicon-unread.svg": "sha256:0b8e572bc60fd3e0e9426154e9c13ffa0bbc5ef34e5cbf4791626bf2048ccf10", + "public/assets/img/svg/octicon-unverified.svg": "sha256:a7c2459bcc20a5c6fbbaeff845a6ff64be3a14938ab5717aa002b2c0b68c9154", + "public/assets/img/svg/octicon-upload.svg": "sha256:3ddcf6560b13eb3918940d11c1d6c0a092e21445cab278a68075e4a1460b5c71", + "public/assets/img/svg/octicon-verified.svg": "sha256:f40d627bdaab7bdaaa2883e90ea382bed3628e11184a1e2b16f302315852f8d5", + "public/assets/img/svg/octicon-versions.svg": "sha256:22870f39c1fa7c8ee6695089cffdd005bc068c11b1d8c941c344fa1d3e8e0ab3", + "public/assets/img/svg/octicon-video.svg": "sha256:2efdb58efb97755062f525af37210d64c7a0a6ea6a5e00d275e82feccd5d1dba", + "public/assets/img/svg/octicon-webhook.svg": "sha256:a6fefce46293b6502111eb6432661acfb47fb4ae473a03f3085b2a46450982b2", + "public/assets/img/svg/octicon-workflow.svg": "sha256:bc2cec301e73b08ee681903278f4dda75c2d08ce80f283986674f113ae429b07", + "public/assets/img/svg/octicon-x-circle-fill.svg": "sha256:dd88bd7e3b53debe6d0f24b63143274a33bcdfb69b5c5ec50a3406bb2e1c694b", + "public/assets/img/svg/octicon-x-circle.svg": "sha256:4b5aeb50376ea1a37e9083faf884d3afb1a49cf8e56e29a75f14e6044eb9e119", + "public/assets/img/svg/octicon-x.svg": "sha256:d0e760af2d00ee136705d444a60096b3a16f251527db9939811f0f739bf915aa", + "public/assets/img/svg/octicon-zap.svg": "sha256:8368177d36739d2958bb1f43e4347295ab0523a053f62dfd91c11724b4bb5807", + "public/assets/img/svg/octicon-zoom-in.svg": "sha256:3ad263ba27d233f234397e9d7048f545fa279f50820b3316b2d52b5624faafec", + "public/assets/img/svg/octicon-zoom-out.svg": "sha256:c277b044faf5fa43a9327a761ef9c99b8f7bce789f7991ed4d2a40e665ec8126", + "public/assets/img/telegram.png": "sha256:bd6c923a59e2209bbb152ba44c93bd5892faad15a6eff143479a9d9daf7c48c2", + "public/assets/img/wechatwork.png": "sha256:1858304a5c4c9baae7e249b8e392a3a44ebd2343f8f644bc92598293e361e4f7", + "public/assets/js/------.f002cc7b.js": "sha256:42d4acf8a7a0f0dc01802aabf6b064b4caa19559625eec477cd934155489be93", + "public/assets/js/----.d129688a.js": "sha256:23b8b49fe253e07d513c2b5ba919f3c165132fd240a33e54d49340bb274be7a2", + "public/assets/js/-.2edbd42c.js": "sha256:8d2114e4f6c7e08ad2a00188a23e712f64a728ad2e655d4e895a8a3ba254aac5", + "public/assets/js/asciinema-player.611f6fe9.js": "sha256:85d9e9be737a3153f14a68a7c45f2d038bc4f4b792d2c86f496f217d91404e6c", + "public/assets/js/citation-js-bibtex.bbb93383.js": "sha256:cd97da1739b7a7ef4bd985e806df9ac35cc02955f66eb9291d2edc4e90af1ed8", + "public/assets/js/citation-js-core-citation-js-formats-citation-js-bibtex.b0672696.js": "sha256:3a3f1980832977a851dcffaf583d4d3fd63ea46a9c8bbbdca47dc98336848385", + "public/assets/js/citation-js-formats.306c145a.js": "sha256:cd35deffcf5ef129886f7a0641262cd2b836cb02c7e1bfb6182d87d21b54468a", + "public/assets/js/code-frequency-graph.7d6d0c57.js": "sha256:58d635af57b3b26094d728840cb850c49a3124016750f303e925a22e6a70ea5a", + "public/assets/js/colorpicker.f575076b.js": "sha256:ea331878eeb40271e36d7187baa0d609414ada3a9d9e84d4ca466b13381d3531", + "public/assets/js/contributors-graph.5175c5ad.js": "sha256:fe5c81a3a657b7318b8c4314f812c9db80f52ce378c2ea9e6f6c498bbb48455f", + "public/assets/js/dropzone.eba86b32.js": "sha256:056145da74a74c23f866c814a623e609f3eaca2539bf9294b5c6faadfdc69a82", + "public/assets/js/easymde.0c0344e8.js": "sha256:06265a31670ce0b0061df8af093b74d6b2c46515a643618578f68eccb070de71", + "public/assets/js/eventsource.sharedworker.js": "sha256:91e1e25149438698f08ed07184fdd46676530bf94e2d60142660fb39e10d9bb1", + "public/assets/js/forgejoswagger.js": "sha256:17ff51675b83837ba0b4e25ec56cbdfaac309f2e4da5da1ec5cda760586bfe39", + "public/assets/js/index.js": "sha256:c5777c08247eaeb5161b4f6853c1aa726c28ec11327d3dbab162387ad708b844", + "public/assets/js/index.js.66aed7fd.map": "sha256:edd7957d643f25cd858e9f00c89a0c8aa037cc7be2e1c44ab3b04990d48e0574", + "public/assets/js/katex.c88d5be5.js": "sha256:7ce647d817455062e79b8de136d48620daff1f0d61e08fb8be1ae2c270d2cda9", + "public/assets/js/mcaptcha-vanilla-glue.a1ccbf9d.js": "sha256:175436ce7f8aee7751ebf5c59b52168313949e142d7438e749b8e4d33ec293a8", + "public/assets/js/mermaid.b6775170.js": "sha256:aa084309b83248d9e9163c206d172ae2bcd24b54daee0f4f3895c527a05fb22f", + "public/assets/js/monaco-css.77228616.worker.js": "sha256:b02961fcd6f878997230b818871928d240d1e1a4f54a60f64a83d9cd6e15824d", + "public/assets/js/monaco-editor.669bd6b3.worker.js": "sha256:c6b1800dbdf732f7aeb2e3f21981e61270e322e33bcee470a4bef0a6cf35dbb6", + "public/assets/js/monaco-html.2f5aec9d.worker.js": "sha256:6b5159199f338a0d23d473dd4897c9853fdfed9ab7a8ed13a996423116d2980a", + "public/assets/js/monaco-json.b4f4ced3.worker.js": "sha256:0dfbeb637d260172ed432ac48b3ae48a5cf719f6e03bb1f16c72a1c7f30ca795", + "public/assets/js/monaco-language-abap.0a1e9aba.js": "sha256:0afcc3bb9d85d1407fe98a3263887f0eaaa0599cb1f6e068ccc3c6a32b054120", + "public/assets/js/monaco-language-apex.f419fbe0.js": "sha256:5775a05163c517d0d82ae06be44b2744caebbb04c10e4a35b92c956a38def8e4", + "public/assets/js/monaco-language-azcli.836f3fa2.js": "sha256:8bfc60d4d245601da2aa26191d3f8805e01416109029d7d6c0a6b0ffcea158f7", + "public/assets/js/monaco-language-bat.84fdae0e.js": "sha256:7231effbdbb7e20b247ad5b863687ba317fc38ecb0ecb364a371d80ff88c6b24", + "public/assets/js/monaco-language-bicep.d97343b3.js": "sha256:d648a49d1c2c3d640de930d202a39c09d44439389297b5412dfb61e0c15f709f", + "public/assets/js/monaco-language-cameligo.5060e489.js": "sha256:58b008397574d59deb34bf615c14efe6b0e8f34d38ef2214d3eb895c674c7cf1", + "public/assets/js/monaco-language-clojure.0d142a18.js": "sha256:36f4711e63c1e97b825ace08457098ee5625a7c0a98bdc67bcc131c3a7e8a805", + "public/assets/js/monaco-language-coffee.2cdb145c.js": "sha256:6c89b8bfeeb2af7f6e50f37a7e01acd48ac50f3120c59dfcd53d735af193df72", + "public/assets/js/monaco-language-cpp.c52bebe9.js": "sha256:e99189d894bf1c1e11ab400e8925c4f1a8d9ed2f0379bc73b3c53485d75210b7", + "public/assets/js/monaco-language-csharp.c320efd1.js": "sha256:6e3f3600051620fa00a3d354bd40a60bd77b0e9debd407f1e350870ef7abc129", + "public/assets/js/monaco-language-csp.ada44939.js": "sha256:3d0e63d12429f913b68ee0a25254985881d0e61296336ca7e7c4d0fa6399d81b", + "public/assets/js/monaco-language-css.1f81ea27.js": "sha256:60bf3de25e742daf0b456cba8d8a5bf46a0e4be85c6d84a998956d9b3d3b243d", + "public/assets/js/monaco-language-cssmode.54d2a6e5.js": "sha256:a9686f8ecbb76cddd69aa3d4127ec3f20cc23a52de55302ff8e34844ab9ce449", + "public/assets/js/monaco-language-cypher.c89bf24e.js": "sha256:0b2132609b9f1dd52d1abdee45cfcfd531f776cf6e52af3ea1d4a8907a5e327e", + "public/assets/js/monaco-language-dart.bf5cd1f2.js": "sha256:745c65860c2ed2c4f1e500f489b9678260b9d21bdb97e2562a56ed940fc1cfc5", + "public/assets/js/monaco-language-dockerfile.6b5e3486.js": "sha256:edb149648cfc020a0794916ff8bce8963e7bda8b869b4534942a846d81de2f15", + "public/assets/js/monaco-language-ecl.2e23027a.js": "sha256:15438c06033726434fd1d2455f94de4694d50db02d35fe0c486a0955d6f813af", + "public/assets/js/monaco-language-elixir.45735aba.js": "sha256:f04cdf50dc488dc73aa285be07b3ca572ccb5a6fdbbe290b8512335bdf67d3a2", + "public/assets/js/monaco-language-flow9.ea377382.js": "sha256:dd56acd415c0c0910a81f8ba583193589f5dd4ed22de39703be5f7b73e3a2d48", + "public/assets/js/monaco-language-freemarker2.5bf2e5c2.js": "sha256:22ab9c7ef2ecdcaf75cb0cab5f7c31a0fbfd65de2295e0f59b462144a4d326dc", + "public/assets/js/monaco-language-fsharp.13824c4c.js": "sha256:3961bcabc88d37b837e5f7b91e4102d2adb839985440b7a2a472b3c5e4f2c6a2", + "public/assets/js/monaco-language-go.2aacaa37.js": "sha256:986c8f368785f3bc615e42f0b02b8804e636178e7f621951bcd8b14cc38f6a19", + "public/assets/js/monaco-language-graphql.0230d82f.js": "sha256:9d767a1eb6ed67b7f33faf3c2efeed5b1f706e345b800ce1c8ef0330f6a24714", + "public/assets/js/monaco-language-handlebars.1a651c81.js": "sha256:6c5b0a959787845acd375d3bf2ed910d8775dbf3b4df48787e8d614813e2e63d", + "public/assets/js/monaco-language-hcl.ce3a176a.js": "sha256:3be51756bf463324e908a3aab4e60af1b350ecd2d35d2c35c2cdbdccbab8fdbe", + "public/assets/js/monaco-language-html.f3b978e0.js": "sha256:1927bd7b068b7a8d43363064be7480559fbb852a644a7ad2c4906c90bbc0f110", + "public/assets/js/monaco-language-htmlmode.9ec3dff2.js": "sha256:c74ae0b53f0d4d8348783440dd13ff4de9d2604db7a87451c2d786c8a7cfb152", + "public/assets/js/monaco-language-ini.f94521e7.js": "sha256:6819b365c348de05f84073fdbf3b1addfca52be57f713f97ef78897e5433c89a", + "public/assets/js/monaco-language-java.99dbf184.js": "sha256:fd311624fd3371a488f7ad4c37fd3945e0ef0667660745f92387ed19786eea5c", + "public/assets/js/monaco-language-javascript.70b86430.js": "sha256:c922ba717573e7fc5e0e139a02474492879d871ff3a2c36f1fd6b9d31581e7bd", + "public/assets/js/monaco-language-jsonmode.01c8479f.js": "sha256:afbc1903f99cf15cc9e3725ec5d0cfa3bc275c23c8f5660a9625c67eadd3d6fb", + "public/assets/js/monaco-language-julia.65e65a1f.js": "sha256:465b54770a0c93fd2295a56d36842c542893da8ce8ef45058afc3f20dfeabeb7", + "public/assets/js/monaco-language-kotlin.ad994b80.js": "sha256:8baebe219c7a9acaf8bfe1a4420b8a6446d1a7d7e8feead3482b3482d9ea081c", + "public/assets/js/monaco-language-less.13963e48.js": "sha256:e15fa84b1c7dd65a75c3f01a98cd913af8fc28e43281218d5b1176f8f3390d18", + "public/assets/js/monaco-language-lexon.5780a734.js": "sha256:907fd71319791787441ad376c30d3559151fb508e1e7212f4072a9a8716bc1f4", + "public/assets/js/monaco-language-liquid.7d13cb0c.js": "sha256:fd62b5cc96f51b7ba99c77c3dd451cd2f6f1a93e9db7b5197f10727068e6f7f3", + "public/assets/js/monaco-language-lua.b6a07ab0.js": "sha256:35a25877697836311a6a471b7fac0ef3c1fa60075bbea3ace4267f46195fdd5f", + "public/assets/js/monaco-language-m3.891db5e6.js": "sha256:c63a4c5cebcc2890b7a173d3a9097ae2be6b2da149992cc237543105c53d4058", + "public/assets/js/monaco-language-markdown.6e4f8be4.js": "sha256:f5d165cf08d7a64c9804fc51f7742d07ab59224ab630198bbbec71e03ef4486c", + "public/assets/js/monaco-language-mdx.d71fa768.js": "sha256:717364608bdb624114c8b52bedacfe86bc93ec65edfdc5c0906c160681d7ac7d", + "public/assets/js/monaco-language-mips.660763dd.js": "sha256:7dc41c52fccbf40d304c1c3da356821a4e6f9039f6a965d829f6d7b2a72cc071", + "public/assets/js/monaco-language-msdax.6ee350a7.js": "sha256:4125fd5f0a77b748c456c2c411f623cd69cf68d5a80b0c4125f64240cec3b9b7", + "public/assets/js/monaco-language-mysql.9216747d.js": "sha256:5a4193cab2ba08d6d831a396e3125ce921afd3870e4ffaaeca425d2c3de02312", + "public/assets/js/monaco-language-objective-c.9219e0ee.js": "sha256:8a221c217e78401626f2cda0fa903c3e0fb5aa7e142416235f839c49cc636f8c", + "public/assets/js/monaco-language-pascal.d42a74e9.js": "sha256:bc7c1436f7985719c53431086a040eb0aa4cdda482d74bac4bcb23c47e0554db", + "public/assets/js/monaco-language-pascaligo.14591c1d.js": "sha256:9b8b04e50faf58b9d5cfc745b1503bab28ebd48f526000c74ef3802e7069a4cb", + "public/assets/js/monaco-language-perl.bb510ae7.js": "sha256:4678a776410cd117342decaf2ae5d022173a6b1d6ea90b029fd7d716a3b76ac1", + "public/assets/js/monaco-language-pgsql.8da8d998.js": "sha256:61d3a4456d4b27fe77fe945686f64b63a9a73a02068eedaa1e3b7a7cec62623f", + "public/assets/js/monaco-language-php.3da1d2de.js": "sha256:55ec01dd7ef966ce7a286b5018f5039ad2453d6c0ec6a5578e53e4758675c623", + "public/assets/js/monaco-language-pla.ef8bdc3f.js": "sha256:832bfa41586140c049c5a1a8dfffa42e39e6b7ef7cce2d8579b4a4a5f2094495", + "public/assets/js/monaco-language-postiats.0d981071.js": "sha256:7edcab8f2bd646abd8ee597b1ab99c4e1ed4980a27c6dd8597aff22d95feb09a", + "public/assets/js/monaco-language-powerquery.8eb720bd.js": "sha256:0db24099f18e1a1c7bb2e3e7f2e4a6e7d312601fbdf4d899f2fd24ea5eb7feda", + "public/assets/js/monaco-language-powershell.05315270.js": "sha256:9899618462a7345463f32ad173bc601411e646f9d5ac86821017dcdb8cd00fb7", + "public/assets/js/monaco-language-protobuf.9d2c7808.js": "sha256:daf33fa18ff505aa1e77a23f0d1a7398619a1fb36689ad1a58ae23be7893d401", + "public/assets/js/monaco-language-pug.778a0879.js": "sha256:67b0a34606c1e8a0b7c153709f780fea44c1214b6feed802921ec34b766b9281", + "public/assets/js/monaco-language-python.995f88b9.js": "sha256:473431d6b18b63082415a44f8f0571bd3afe9503e35a49e9b641b3099649e5ae", + "public/assets/js/monaco-language-qsharp.8e9553eb.js": "sha256:7fff9381931226ae533aa144ff297d3ea93472c2c980870e2f57807c976f6cf4", + "public/assets/js/monaco-language-r.92ea0eeb.js": "sha256:2eee9f645447ad2d245884efb15379bd588f99f679fd517f0c74d6bc610a7d73", + "public/assets/js/monaco-language-razor.4f6ab070.js": "sha256:eb21b551e158342a49681866b0777d38c71d515c642b655d3137162097763811", + "public/assets/js/monaco-language-redis.9a5fb087.js": "sha256:0bb2825544e04a78e338e254d85945cd55889c41c1766ae387c525eb41ccecfb", + "public/assets/js/monaco-language-redshift.c904e9d3.js": "sha256:1b1293aeddb8785053adecf96b3d7f88ef97c77bcb615063fd39d38a61fa7500", + "public/assets/js/monaco-language-restructuredtext.bf7dff86.js": "sha256:e75b1513825811f327d67ed048995e7cecfb8268d594d7c4fce46af69ca5c223", + "public/assets/js/monaco-language-ruby.70e690a1.js": "sha256:df1597cf352ffc49ed36a4d52f3a7389431144b8fabae48d58cb32d9ee05ffb2", + "public/assets/js/monaco-language-rust.085044b6.js": "sha256:6be358e3e7d1d1d0eed04c939ee65d9d965f94f4e979c05501fe63fea4326caf", + "public/assets/js/monaco-language-sb.403825c1.js": "sha256:419cf0a8e57be536e245f1d4a98a0b78c9bf1ea40bcdc749ed659a31ddbc9a1e", + "public/assets/js/monaco-language-scala.1eb40271.js": "sha256:20deb60bad1711ac862b45e311c6cd3ec79b0a6e60801585023b958979d42aeb", + "public/assets/js/monaco-language-scheme.266a26bd.js": "sha256:017cd5b07415bfa32ec26a2beb37bd8813564d52e9aa78ea581685f463da30fa", + "public/assets/js/monaco-language-scss.f87505cc.js": "sha256:b6e2bf41170fe7437d9d240763c9adadf45e4f105d303d5805a6303b31dbda4f", + "public/assets/js/monaco-language-shell.5df5d386.js": "sha256:218f6ad54c2878459d1f12abb83d681cd5f726e9ad2429337666b80bae9251d2", + "public/assets/js/monaco-language-solidity.19eddfcf.js": "sha256:d24be3117854bd0fcde55157736584ccde782bcae4fb431bba92f924551c8cc4", + "public/assets/js/monaco-language-sophia.d0b16b6c.js": "sha256:e542a06f6ce13dd6e05c8b0e4f6fc5db475bd2ad95755763f911bcd909098c6a", + "public/assets/js/monaco-language-sparql.6708d955.js": "sha256:61ccc508d66399b8a6f2aee1b255fca529e90a1300dfd3b0c28aec83247f0a21", + "public/assets/js/monaco-language-sql.31a34c8c.js": "sha256:d1f4cbc031bc5363d5f3e75192e05861e4876c278c70b235172470523c50258a", + "public/assets/js/monaco-language-st.b6a70301.js": "sha256:67c371954f35a39511eb60056574272f687e84685ed8c0042270c39de1a9907b", + "public/assets/js/monaco-language-swift.baa9b1ac.js": "sha256:d95a672aaf6593ff3306a42d2e23bdee3fd0657e17c8b9de9bee6deb3d0bc7e0", + "public/assets/js/monaco-language-systemverilog.7fe974cf.js": "sha256:13f75473ccbc089c833493f9165c34b031f9e2b5e8a18d053430f52837d889fb", + "public/assets/js/monaco-language-tcl.042acaac.js": "sha256:892e2455ea8837eea59672dedb9741f53556a618e9679d103ec5527df439a9a6", + "public/assets/js/monaco-language-tsmode.f7c02847.js": "sha256:6ac626cb46bc39047e023a964884f3a602aceedac66ef1778b5833e65ebdb092", + "public/assets/js/monaco-language-twig.230cf148.js": "sha256:a44279686b2cc4c08bc85efd295135cfe914ffd955344f5f6fc249f3ce3d08b6", + "public/assets/js/monaco-language-typespec.043af67f.js": "sha256:d44210cb54311bf3859c902cb300b9a7e0410a69679c5a6ea8f8563df85e66f1", + "public/assets/js/monaco-language-vb.66e33866.js": "sha256:f75259cf97a58a4d82a1bae602dbcfb690df5b13fe664cbff984764797633e9e", + "public/assets/js/monaco-language-wgsl.4096b750.js": "sha256:e30cd8303f080b43fa213fac763c48a1318e7e7ce521b7b44df1b6e8b1f14e53", + "public/assets/js/monaco-language-xml.4a113a3b.js": "sha256:2bfe5e6cc8156bc60ca1f43fe9ebf3f518c57b0184e5a4a921eecdfe9e9dbfab", + "public/assets/js/monaco-language-yaml.cbd0954c.js": "sha256:f6ba08f640b583808f4c66bf5f46a61fbad858359c0891b13f27fc50d5a4646e", + "public/assets/js/monaco-ts.2e16d925.worker.js": "sha256:4dd3cb739ed6465fef42e2d885d2d670062e7fb03d20fed5ab086d421262655a", + "public/assets/js/monaco.140f2e2c.js": "sha256:3a58628f1997df618a31d6ac37ba82d4f23b9e8c82f09374eaf22e669f18306a", + "public/assets/js/node_modules_mermaid-js_parser_dist_chunks_mermaid-parser_core_architecture-I3QFYML2_mjs.f984bf8a.js": "sha256:89652686c58106e32d21a089f4bbeaae8a74d7ba69c85489d06abe31adfe13bb", + "public/assets/js/node_modules_mermaid-js_parser_dist_chunks_mermaid-parser_core_gitGraph-YCYPL57B_mjs.e0e06234.js": "sha256:4ff46e3da61fb153d30931da147ca614a779ba075d3509f115369f53e358fca2", + "public/assets/js/node_modules_mermaid-js_parser_dist_chunks_mermaid-parser_core_info-46DW6VJ7_mjs.ee24fbd3.js": "sha256:fd403bd515d8b5376c6e126de8af3c38cb1246ffd0b1b8275925534a2a5d0027", + "public/assets/js/node_modules_mermaid-js_parser_dist_chunks_mermaid-parser_core_packet-W2GHVCYJ_mjs.7aa017a5.js": "sha256:36a54cb7f35ad914fa6cbab90fb274d76828327012c0f75530f6454569facc41", + "public/assets/js/node_modules_mermaid-js_parser_dist_chunks_mermaid-parser_core_pie-BEWT4RHE_mjs.537545da.js": "sha256:9a252a1033cb1b6d17b84d0a0e5ff687eb590807c757cf742e8c55a8fc141b3a", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_classDiagram-L266QK7U_mjs.96171c79.js": "sha256:aa6f3b85242e0f15d24f133b0885c576baaad3458907dc99b1adc84318dfb969", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_classDiagram-v2-JRWBCVM4_mjs.8b0bd6eb.js": "sha256:abdbd2227412dd7d5b6762226115ccabb715e37bff3f9f944cc1db3625e592bc", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_diagram-DHSB7DV3_mjs.c74fdcd0.js": "sha256:d6fd9482d39e76206b6a4704649f675c92051e04ec14ac40f8d1d463b2492b25", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_infoDiagram-SDLB2J7W_mjs.f5409c51.js": "sha256:1b00dc000a0ae9941bf395dca50a24281fb8660beb11e86872682d3107af1195", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_pieDiagram-OC6WZ2SS_mjs.8384870d.js": "sha256:89129bd8ccdf5bebc9233272c8554afbb66f7a4f1869c09e7bb907a6548ed635", + "public/assets/js/node_modules_mermaid_dist_chunks_mermaid_core_stateDiagram-v2-WR7QG3WR_mjs.68b5eb63.js": "sha256:3a407f3a072abaaa5b3b02e637356c6d1e0fee4754172f46845dc17c1940724a", + "public/assets/js/node_modules_monaco-editor_esm_vs_editor_common_services_sync_recursive_.7231c873.js": "sha256:97c4007ecbf772512b51d24e2184b0704c7669a92da12dc71eee68d7ae0a5750", + "public/assets/js/pdfobject.10584a35.js": "sha256:9b4a237ad56a5c5b21f73f8e2b7c97f9c0299ec28708bcb17e47e7578e8f6445", + "public/assets/js/recent-commits-graph.ea7e79bf.js": "sha256:cd4d57f22718bcb30387339ef67b68a21f933a4b39400260ba148dd8d796af17", + "public/assets/js/sortablejs.9ea09496.js": "sha256:5932f2492390c10338b6e5e3e427633f440f3720c8b4de24166a47acb0e21215", + "public/assets/js/swagger-ui.861b02df.js": "sha256:254822a4aa4b50719267b2196823182fc2085adc9e903864dbb9f369e6f65cf0", + "public/assets/js/swagger.js": "sha256:29e88a462fbc9597106992c38255dd96336f7f684d96934a23bd22a1afb67ffb", + "public/assets/js/theme-forgejo-auto-deuteranopia-protanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-auto-tritanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-auto.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-dark-deuteranopia-protanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-dark-tritanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-dark.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-light-deuteranopia-protanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-light-tritanopia.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-forgejo-light.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-gitea-auto.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-gitea-dark.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/theme-gitea-light.js": "sha256:c4796e7a20fd2842b8bfcbbabc1d54a0d246521402dd03f9840da40b4942efd7", + "public/assets/js/tribute.7f74a553.js": "sha256:213282f5890ca403f5ac1f9e84b38f260e42028030dbe02b9953ca8127ddd584", + "public/assets/js/vendors-node_modules_katex_dist_katex_mjs.539c7d22.js": "sha256:c93920c9ad13dfc5e9d2034fd0ff8965bf3bc27af1ebb2d6dddaf5d80dc85405", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_architectureDiagram-PQUH6ZAG_mjs.df9a7337.js": "sha256:96e75a3e51d218ad87923918e3a306e99e3b16afbbc8e15bb1aab5cc98320505", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_blockDiagram-5JUZGEFE_mjs.acd6afb4.js": "sha256:3daf16a9f2d2cb19b1af6191fc84b07e1ad7586672b683ffed06f9253b718710", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_c4Diagram-7JAJQR3Y_mjs.913a4bd4.js": "sha256:db686b60f65e79c53c1cfc90d1c7636062ffd6e957078ed4782a55d26d4482eb", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_dagre-QXRM2OYR_mjs.a9a27427.js": "sha256:5c791e803037a6a95b6e46a2fefc64b5e68f39d033318694cb21d37da346be51", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_erDiagram-MVNNDQJ5_mjs.a009299c.js": "sha256:32d79740a1e89e15526655e8054b957314cdfc1d5d1b2c5570f5d0354e0260cd", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_flowDiagram-27HWSH3H_mjs.0804d870.js": "sha256:0b708a9274194e9218e906cf9b819faf3773ef145f85fa792f4db0bbf49f3274", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_ganttDiagram-ZCE2YOAT_mjs.6bdf0a44.js": "sha256:d7b6eb0e74d1bc53952e9e6c53b926530d4acd77235c8c993fd638d2d0b65a5a", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_gitGraphDiagram-ISGV4O2Y_mjs.df4bc375.js": "sha256:04c7a8227f676e6d78d3c81646cc95bda554ba1c6c2280b3a4827551db77a07f", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_journeyDiagram-D7A75E63_mjs.f1021023.js": "sha256:339fcd9e695b58b0556324b27f825d122d03796ffea0abebb6367d8341f14a97", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_kanban-definition-4GR4SRK3_mjs.96f62c23.js": "sha256:62571d6bfb243dc9fda2814e5696854be858107d71f0b8417caf2bf92a0faaef", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_mindmap-definition-7EJRZJGK_mjs.2f13b0dc.js": "sha256:847cbdfd9133184d6faff446733e092d9cc3db39b69ab3956bb47db986ce54c6", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_quadrantDiagram-OT6RYTWY_mjs.b5c6b69d.js": "sha256:7a986c175e73565e551dc9a407c82a51c9976786cc597b299658a4332ba6f257", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_requirementDiagram-BKGUWIPO_mjs.97d96d8f.js": "sha256:de929e7aa147d8509152041ea6497f5a02298586111ab3c60a17cf2ca98937ab", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_sankeyDiagram-3MH5UGAL_mjs.82d9325b.js": "sha256:0f0caa88d228c1f1474544514ff85cc1d3fa00339ff56a0ff4b95f4b5c7eb882", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_sequenceDiagram-C4VUPXDP_mjs.1d31428c.js": "sha256:c7519d29b9ed38da88d3b8b7530b27c3b2b86003989028e7a462d6e425129550", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_stateDiagram-BVO7J4UH_mjs.cdd7f82d.js": "sha256:0639f6f92284066ea3ce9af021b18ebf61ac064c6451e43e099517a3cf02ff9e", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_timeline-definition-WOTUTIAU_mjs.30b8e67d.js": "sha256:cc9d44d081283b0d04664cdae22e7d3822e7d5046a77b8c6371485624758c80c", + "public/assets/js/vendors-node_modules_mermaid_dist_chunks_mermaid_core_xychartDiagram-NJOKMNIP_mjs.9e5ab07d.js": "sha256:56fdbce49955953942855e9dfe8f5c77f90f25274a159804da20b3ed8eecc8fe", + "public/assets/js/webcomponents.js": "sha256:9ee66700923a9d1a1b96df888ad231d6a9d8d21d49b66068ee82bd5491f2aba9", + "public/assets/licenses.txt": "sha256:3ea70ed4d8dab4fbd83f67640aa20e49c2e29d6a410b1f1d3b84b4f20a4258e4", + "templates/admin/actions.tmpl": "sha256:3a87587d4c8c35b59722c5dbda14bf6c55b1df881f2eaae7b8fd16cab7e43adc", + "templates/admin/applications/list.tmpl": "sha256:30c2f9040fba74ba77bcf3f17c4ef191b827a1aca11b43f190f6fec0716b8cb9", + "templates/admin/applications/oauth2_edit.tmpl": "sha256:4bf7bc194d224bd5093755568c72e051d0f42b9738ca1dbec2224e46db1130d5", + "templates/admin/auth/edit.tmpl": "sha256:dcdc252775d72e6964af4d6557b47fbf7358fb1f6a2f173ea5a396c1df8e32f5", + "templates/admin/auth/list.tmpl": "sha256:3be096d353ffbbdf11eabd0d31a79968a94a1605428164825a841d1d392cf501", + "templates/admin/auth/new.tmpl": "sha256:79e61d05b7af4277177b8c6ae434571dbb2948ab07f69bd00f1bc297ad1dd230", + "templates/admin/auth/source/ldap.tmpl": "sha256:2ef7cc111d7196febd407368b3e199cc4c4690153d684a4f1b372b1c4e9808b3", + "templates/admin/auth/source/oauth.tmpl": "sha256:353c18b2d39f12e9fb9435b9d0e0991e3715afe00ba54293c3bca1a24e43d42d", + "templates/admin/auth/source/smtp.tmpl": "sha256:71c81a0df9fbb4529767da207bd2e08d1bb397455b8ecd8df4b8f2774e0d2f11", + "templates/admin/config.tmpl": "sha256:af529421ed499009ab8e75af91f99378137720230d75fff02fe617ff61c7289e", + "templates/admin/config_settings.tmpl": "sha256:a4431d8decf8f66b4c0874ad6036b7f80f610bde598c1a435f2f99d15df5237a", + "templates/admin/cron.tmpl": "sha256:bab24f20e0f4ddadc3a4205f4da880fb340f69454e94ca3e81ce55a139af8ac2", + "templates/admin/dashboard.tmpl": "sha256:6686362480bac1bcb828ade784660047f3c15c8fbcfb59f83bfb5dbfed3ebaf5", + "templates/admin/emails/list.tmpl": "sha256:9b75f570c3ca501faa23401e65f8cc33475031036878978640463e0fe78425c0", + "templates/admin/hook_new.tmpl": "sha256:df6e99264a7a184db816ef06658d9d6034f181b681c8a7bcfd20c060bd10a674", + "templates/admin/hooks.tmpl": "sha256:41e00e816b21a948142d957476279a7622ffefb312fef5a295823a1584ad2018", + "templates/admin/layout_footer.tmpl": "sha256:4d05d23b01b287951e96a1e731421effaf6874d2df0be3d004bf2d6df69f817f", + "templates/admin/layout_head.tmpl": "sha256:71fb21cdb74bdf890fdd0e0b56522437776cbddb7c289e8663ec3a00c3b37c0a", + "templates/admin/navbar.tmpl": "sha256:167c4d4556b50551a2de3f1eacb3fa3b41a54031c15e82ff4f9128e9390f7726", + "templates/admin/notice.tmpl": "sha256:fef869bfde39b68c3f8792abde0ef8fb3e21467ba7d5c4f5f1fa6c3adcbb794e", + "templates/admin/org/list.tmpl": "sha256:a55c4cca68d9f9b102995a39df17f3772fedf4fb8086d88d9fe8b4598d474bdf", + "templates/admin/packages/list.tmpl": "sha256:5cdb1d6f4d6a3c98dd58aa8e8166d26e868f5e39ea5b565cea4db5aab194f1bb", + "templates/admin/queue.tmpl": "sha256:3c6cfd943d32b45d9f2c58633875d4ede46d14be1c31745720fe72b99fd5ed3f", + "templates/admin/queue_manage.tmpl": "sha256:db06eff1e2a9cafac4565f810cfd6bed86631d74015a8edc9ad4a003358902ba", + "templates/admin/repo/list.tmpl": "sha256:dba746c578a26f6bda36b2f46b5fc153257b08f613b0d9be60ab62ba6f4862f0", + "templates/admin/repo/unadopted.tmpl": "sha256:81bdf85978b52aa77ad404455d4a87b23130822ec41d53035308cfaeb5a6c833", + "templates/admin/runners/edit.tmpl": "sha256:5cb40aa290ad9b867a39f962ab510f8c5dce3597ffeea5020c0490e2b49a6edc", + "templates/admin/self_check.tmpl": "sha256:d3160640928f95ef34c7a44767ffec4f1a8406ae78b67d3523f97d6820703c28", + "templates/admin/stacktrace-row.tmpl": "sha256:db97574cbb0253956fcf8f1c7256f12cee6323481a4f2b7c85888aee8b374ee4", + "templates/admin/stacktrace.tmpl": "sha256:8b7c33a23183d91b016939c499f000f785a510ad2127f60741d65e845f0ba91f", + "templates/admin/stats.tmpl": "sha256:aeb43da87ee99047042d7d94ba8284ce8979f3640536ff272cd52c9b880ab765", + "templates/admin/system_status.tmpl": "sha256:a7f1aab9c63a38cd005729f495568ebfdaeaf71e4dbb14fb48a1482a511f1241", + "templates/admin/user/edit.tmpl": "sha256:defcb5ebe81d2aa3babc810b13261baddf7efee8d1008b5927439cf634090eb0", + "templates/admin/user/list.tmpl": "sha256:76343cbc57e6b17587e460685708dfb664eae48ff67c98cdb43d806b1017ce65", + "templates/admin/user/new.tmpl": "sha256:ae5803af43862be23446893a291e970e1ada141aa440bc12cdba4617c663fdd3", + "templates/admin/user/view.tmpl": "sha256:e0ce958f8a0e64441028df1985be2445b858b1332e7e93195d224d19d7b13c2c", + "templates/admin/user/view_details.tmpl": "sha256:cf2a87a2d95cd8be7db00023b987e07a5dc67e493bf83986f62c87c7e2988500", + "templates/admin/user/view_emails.tmpl": "sha256:9197b7ee88538de2fe2e8a82138b1d9197f295bd08f7587a6f421da97fd404a1", + "templates/api/packages/pypi/simple.tmpl": "sha256:6e5d8978b0f9abe707ed518955e7af8952287b02aca45505ca183e5a9154f61a", + "templates/base/alert.tmpl": "sha256:0d64d508b32f543da7c4e35800d61884a641a14ca495f8ba86f6488a2aa114c5", + "templates/base/alert_details.tmpl": "sha256:d9cfbd9fe0106122808739ddd1c7f80af158594a072c50f2ad96095aa25af515", + "templates/base/disable_form_autofill.tmpl": "sha256:74075807d0d9bc681d2f63f70352b9e7bd353aa2d1bbe7c0c716e16c0554e64b", + "templates/base/footer.tmpl": "sha256:03c21cc7e120d3d87944f41505b4866ca501486cc71293201e3c4db82d923ef6", + "templates/base/footer_content.tmpl": "sha256:62a5c513bcac036a425c38d5fe139c5184d1de12f44d951b855bd62ca73fb493", + "templates/base/head.tmpl": "sha256:e388e8640cb69924cbd06841989f85153bf3bf511174dc9cd081bc0d4ce8a2cf", + "templates/base/head_navbar.tmpl": "sha256:9ded2246d2cb2a705964cd35d455b0761c8a1a08aa87dc582b2cf59446769c1f", + "templates/base/head_opengraph.tmpl": "sha256:40d44ad7948664c8066e35b170b4fc8f7e0a9df0dfecfe937c44cfd702bf907f", + "templates/base/head_script.tmpl": "sha256:8837faa5e373c66b705c7afefbb9df68f7fcb2e46d8bcd0d1423086997278990", + "templates/base/head_style.tmpl": "sha256:3afb35a7726a20d67d5c3e2e0f619b87869b67922c472448332bde5e3ab74b5c", + "templates/base/modal_actions_confirm.tmpl": "sha256:7d82485c26bb6ae0a684248413f63f8eb3c021c8b11de7faa29396114b7788ef", + "templates/base/paginate.tmpl": "sha256:19a208aaf7d256ff2a2225e044430a5da139e2134da17e325e85462ed05ec978", + "templates/custom/body_inner_post.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/body_inner_pre.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/body_outer_post.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/body_outer_pre.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/extra_links.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/extra_links_footer.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/extra_tabs.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/footer.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/header.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/custom/repo_flag_banners.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/devtest/fetch-action.tmpl": "sha256:a593788de2dd7859642d7dcbf69ec24afa2d66c7c87a4147c015a1dcbe8ad180", + "templates/devtest/flex-list.tmpl": "sha256:791a8233da694f954ce1480d50ac1e3850f6fc071ea8c5e11634f280bb5a3b29", + "templates/devtest/fomantic-modal.tmpl": "sha256:b13a50b161c31f78f7c3f2d33a2e96ac313512a8e5e9068ce704e96cab325dd3", + "templates/devtest/gitea-ui.tmpl": "sha256:f2fdc36ae484182b6d372d423222f8092cd080ac8085e72efcd61570bec4911b", + "templates/devtest/label.tmpl": "sha256:8f6c22ac1d01e1a16d3280b19d9900ae3b43b2eb9c91b674a1cc101785d8a647", + "templates/devtest/list.tmpl": "sha256:71d400d290ddd7c04693a70f0e26bdba7d1f284b10edbb200bf4667e5e9c813d", + "templates/devtest/tmplerr-sub.tmpl": "sha256:4ec7cfe55a1328e64ead65b851a062543a8067fb05d7e75ff413cdf528845e1d", + "templates/devtest/tmplerr.tmpl": "sha256:c73dda84662c8db54ae97a9f27636ec1eeff920393fafffa5faa65a0e08e65fe", + "templates/explore/code.tmpl": "sha256:64b9351799bac06952196476afd0368c418e432042e085f98aa5431a561df537", + "templates/explore/navbar.tmpl": "sha256:d14483c7cd4005fa3280e16550b49ec86a9a3e26055bc71b0508b59d1441e9bd", + "templates/explore/repo_list.tmpl": "sha256:c2e2c442c427363b568096f1ab521623af3d941b9705cadf01b013d337d5c0f7", + "templates/explore/repos.tmpl": "sha256:b532a473003317ed0d9662c2ba2a739fca843ef15a34564c75f180f472a61fe4", + "templates/explore/search.tmpl": "sha256:6ab4f8d64848c4c4935a45811cf5b571289b401e3b054c48666045a5fb641e20", + "templates/explore/user_list.tmpl": "sha256:e52d5e1996b1ab2f71b0b95e29e3664dee3e7b125022d7d90a8bb2f90f3ff958", + "templates/explore/users.tmpl": "sha256:200e032b15a95c4d30417df0d2ed4f2ad69cbf11e1e751d421ea4c8418fdfd22", + "templates/home.tmpl": "sha256:810cd90701bf38f7c59781e01c7c3c6ce29f44662bb3147076489a55d535f2a1", + "templates/home_forgejo.tmpl": "sha256:a76afbd21d7ac3186b16b6678e2fa86fd8e4c47cbe5c53325c9adaac75b510ef", + "templates/htmx/milestone_sidebar.tmpl": "sha256:49bbbcbb930c23f579b4042a87b5ca9444e35338c56c654dd12ac0d7bf3e316d", + "templates/install.tmpl": "sha256:e70a1e3d415416314c53e6618b53c536e671fe5ea0fc7def207cfd27735f87b5", + "templates/mail/auth/2fa_disabled.tmpl": "sha256:eece48a6609e78c1743b1c420cbaa74acc0cd3823780472060787878cb2f7f35", + "templates/mail/auth/activate.tmpl": "sha256:c044ece624cf53370636f353408117502e402db3508798ffe23b370c39b50697", + "templates/mail/auth/activate_email.tmpl": "sha256:3b9f0942313f396f79e6e957a5dcf8ec09c76d76e65def9c4b2b700efd204467", + "templates/mail/auth/password_change.tmpl": "sha256:59a77dd6a4d320bc3e113dccea05c928146ea15c045ac850b371909efa468154", + "templates/mail/auth/primary_mail_change.tmpl": "sha256:bed808916049a4e3c93f9d29b94ea6656490a304bc5c012c2fbd2377c05719d8", + "templates/mail/auth/register_notify.tmpl": "sha256:7284e8939df2b3f26c967903f62869857bebf6427e7ef42d339db304a9fb4e67", + "templates/mail/auth/removed_security_key.tmpl": "sha256:8b4b8e768c79b700099b2891f42e1febfb0cd0bd9499229e27aeaa5fd6b3cda6", + "templates/mail/auth/reset_passwd.tmpl": "sha256:853961d38c6002aba4b6fcf8f2745a175a7cb75ca70ce24c2e15b594196ba369", + "templates/mail/auth/totp_enrolled.tmpl": "sha256:734949b88da5a0012905882a0344b4d18139dbf530dbed69a1ea48b13f134d5f", + "templates/mail/common/footer_simple.tmpl": "sha256:f5c128a9b7902553022d4f4b1ccf6ce6fd91097ac7a0e1078d0130351cdfff52", + "templates/mail/issue/assigned.tmpl": "sha256:8d71a0163e001d45339484588f119f368cff15e8e3e50e1bb641994b306aecb3", + "templates/mail/issue/default.tmpl": "sha256:ad3eee9b116ebfdaeaa980702e091d47f24694a68962ec4ee11155f02c5c21ef", + "templates/mail/notify/admin_new_user.tmpl": "sha256:f2d9b36c26e4e01d7176224c081e323139e767519481725fbbca07bd7cf66398", + "templates/mail/notify/collaborator.tmpl": "sha256:2e6bed929a2c4d6223624e26b6ce5a60739f20b27616d72b8c3d2410a622f4a0", + "templates/mail/notify/repo_transfer.tmpl": "sha256:e67edd0b99755de74d8aec3f97007669c9c1f1d9e0d5ac4d9b4ea643151d5ec6", + "templates/mail/release.tmpl": "sha256:87a058974cd7ade413076a262b61164a66fcbeca5f9f7ec0ea66467c5ef9c078", + "templates/mail/team_invite.tmpl": "sha256:88917f18a6b151c385380c873f41632030fd3c2dee83f417cb8ca75ac59061d8", + "templates/org/create.tmpl": "sha256:cec6a04b5b6e4713c6b79dac9032de9e36d16430cdb6e04b23228c34d24920a5", + "templates/org/follow_unfollow.tmpl": "sha256:2795f33f10edb6d0190f4b112e662f0d539c06ae0a525b4ae45bae5970baac04", + "templates/org/header.tmpl": "sha256:8889228f047a2def633461c7946114e8e371ee6b486bd9a7abc9c2dac58b2dea", + "templates/org/home.tmpl": "sha256:81c6f0bebab0e9c7dab405cbc877aa9dac066964c5564c217b742f3ed093a4b9", + "templates/org/member/members.tmpl": "sha256:7c5c9dfa0106b8af6782e42d731e53ce97b1250824ce10693e011e6e17616b23", + "templates/org/menu.tmpl": "sha256:a4fc296ab4278dfb3e287ae7c9f39134d6aeba66c825d13ca5d6fea3cde869ea", + "templates/org/projects/list.tmpl": "sha256:70a0226e7db43a2c2f8e431d187867e77ef9bcc21437c6085cc3b5943500e2fc", + "templates/org/projects/new.tmpl": "sha256:13007d3f6ce7bbce6a218213780be1ee1996c7f8e1bf235f772841b78ebd3d0c", + "templates/org/projects/view.tmpl": "sha256:9b3a6295b55fc734e1b823c0b40a9b4b9de4726eed700f2fb929c79738e0fafa", + "templates/org/settings/actions.tmpl": "sha256:65872978725c06a8939e3ca7487fc70e67c47689ec99252b9a042614d7537714", + "templates/org/settings/applications.tmpl": "sha256:ca2cb3c1fd3fc753926a02cd38ffff07fa318fad99e541ed9a904bf8280c1184", + "templates/org/settings/applications_oauth2_edit.tmpl": "sha256:cd3284c4ab41b91fbe04b3cb21b81c56ebae5eb25dcf83d1a04e30cd6f2c5e3d", + "templates/org/settings/blocked_users.tmpl": "sha256:0fe21e5923e422da987a6cdbdf8a8200c96d846a4fe271e340938a227f423d10", + "templates/org/settings/delete.tmpl": "sha256:02734119913f5a764d32f268b80e655aa89d48ed4ac2e35141dd83e68ae086f2", + "templates/org/settings/hook_new.tmpl": "sha256:705404e8051a86d57703f80c7407baa9a8c0c85275cc0f5d1693dc874ef359ab", + "templates/org/settings/hooks.tmpl": "sha256:b279550af060df09f04bbc7b5e213ce45d03902200fa9cb80f5602aa5c0d2c40", + "templates/org/settings/labels.tmpl": "sha256:402cacb8c7581b88ee6ff193be5311c7bd8e8d96b70fee0715c18e87c7bcbb43", + "templates/org/settings/layout_footer.tmpl": "sha256:efff0806f2073a97b0cba12741e1307ab0730947ae9262f9652b95373c084d71", + "templates/org/settings/layout_head.tmpl": "sha256:a24850ec5e6716926dcbe1ef777ed328af3a05ba7a918f162fdeebcdccd84672", + "templates/org/settings/navbar.tmpl": "sha256:30fca0beb22c0f48a11838a161b884768a0cc6a2388a5fc3d74402bd7686517a", + "templates/org/settings/options.tmpl": "sha256:6335baeb1c0f790cf4369dae3e4c66bac33a25acdb0123893c00b6db4ea7b6c6", + "templates/org/settings/packages.tmpl": "sha256:3c85dfe000a5a058dc4f538f23397ec1f0097a68a130e8d9049b9e4c451a0e83", + "templates/org/settings/packages_cleanup_rules_edit.tmpl": "sha256:e4db1b90fb49078a48d25c262212fedd483225b5e8bf868b8c955fcffc391fe7", + "templates/org/settings/packages_cleanup_rules_preview.tmpl": "sha256:76d678f4ce347565a57039df85c51c07f46055b649b49ded41a376024b0ef202", + "templates/org/settings/runners_edit.tmpl": "sha256:8865d9fabddeacc188afef74dc124ddde33e712288e1e6451a8880fc148042a8", + "templates/org/settings/storage_overview.tmpl": "sha256:dcf2d16da64b5bd17e81e1f3df99d7389a31a90a18f7c440aeaa8649e40cd5af", + "templates/org/team/invite.tmpl": "sha256:ed8a591afc1fd1a0dcb5706ed332cf3a7bc5eda0c114e49a5d2738af6c6b97c5", + "templates/org/team/members.tmpl": "sha256:3666ba4f9aefe61258324ed13c6a07dec4648fde6e4f8afc2af5dc19783f7630", + "templates/org/team/navbar.tmpl": "sha256:e2f5a9e635fd89cb45856b2738dbcd7afdba61b7cb0320c5df624407b74f6983", + "templates/org/team/new.tmpl": "sha256:f46ddb1e2d63c3ed8427b7ce0c3f0c2c4a731e5169c9eb5af4fd98acf7514301", + "templates/org/team/repositories.tmpl": "sha256:17e9256ba1db57fa5869e90ac1daa436e2accfd05d709aa87d83ac1f3fd480e5", + "templates/org/team/sidebar.tmpl": "sha256:2c67dd116703c69f5a876db538664540c4f273a5f3eccba454c4ad6b8bb2c5f8", + "templates/org/team/teams.tmpl": "sha256:5c03ee3293357631846ca551ed55f1f6feb50b092ffd0222b81a25896f5346ba", + "templates/package/content/alpine.tmpl": "sha256:a5b7b4b7c9a8ff69662c46f05f1eaa6a24f4dfdd14e6305cb83ef3db69fb3b0c", + "templates/package/content/alt.tmpl": "sha256:a774eeaf66a1836d2d788f9091a42dee7b21ab00223e86ecb7c6e88b23d4dad4", + "templates/package/content/arch.tmpl": "sha256:924a044a73074bdb01d006bb161622c1ed85260829bec9207af175d03f0de142", + "templates/package/content/cargo.tmpl": "sha256:47d986a25603029f8ba4f63ab529c39eb916d5f8a2e9e1f4580a219b03b75759", + "templates/package/content/chef.tmpl": "sha256:31416043190582345e6d49b2bc4249e68f7ee1cceaf4aa20c2e846d548186d01", + "templates/package/content/composer.tmpl": "sha256:0fea5eeea60001fd5ddde3ef123544b03e5927b503974d27250b94ff7e0d306c", + "templates/package/content/composer_dependencies.tmpl": "sha256:e991122a66dfadf9902eb34bd367e81b6435be8ddd46b108c828103a7bf930ba", + "templates/package/content/conan.tmpl": "sha256:385892c3d758cebf5a991c1021a64b3f91a01193b9c7d4f9b03f3ff92ba3385a", + "templates/package/content/conda.tmpl": "sha256:bcd1ecfa7cb06369241f86c3952ce95ce14835df338ff3ef6bf0b87c8ddc5a1b", + "templates/package/content/container.tmpl": "sha256:1a75d9234c75a5030cedcf6680be06081c2b3bb0d6dc25c15af2f2bb00c1a3b9", + "templates/package/content/cran.tmpl": "sha256:77613a3c3132a9217afb1fff5502760eb17fe203ad9e3b73974c09dce5571261", + "templates/package/content/debian.tmpl": "sha256:62a03111b380ba950464282b7d520af00795214716fc3b844bcde3fde186e05c", + "templates/package/content/generic.tmpl": "sha256:6d869835bbebfbf0689ea52e759d8a3b8715b91f6f094f0ac57629b52db53b29", + "templates/package/content/go.tmpl": "sha256:87486bfeaf885756adb75b47d9a34fac689918876d4abb186dc54b8065fd12f2", + "templates/package/content/helm.tmpl": "sha256:fbe6b6804377c0af3fc76dd42be68d143261910875e935e05f5ce16208e28b50", + "templates/package/content/maven.tmpl": "sha256:2e8ad70cc10c0ce9824f3e2941bbdf44a59d7a3c04486fce9293d024d0a49f7c", + "templates/package/content/npm.tmpl": "sha256:475210baf30f4ee68e8b0730ab1a049f9a21ae618a0f5b871729bd43ccf6d464", + "templates/package/content/npm_dependencies.tmpl": "sha256:e991122a66dfadf9902eb34bd367e81b6435be8ddd46b108c828103a7bf930ba", + "templates/package/content/nuget.tmpl": "sha256:f112a16c86985f691e39df1d74b18d24ae4dacbbe2948b24b2338f25ba9ed5c6", + "templates/package/content/pub.tmpl": "sha256:1d686ac5524c97f505e8b994279e56973c8eefbeab02d707fb7c6492944168d1", + "templates/package/content/pypi.tmpl": "sha256:6a8c8e6893dd5adae9f2b273da3734f4f76ae00bd4a026251db06d052340b9e8", + "templates/package/content/rpm.tmpl": "sha256:f280467052125c4e29cb247f4af01aa3af7624fb6e9b35ad9be0d96362b2284e", + "templates/package/content/rubygems.tmpl": "sha256:d7214c6ce158aa7c54c83e3b5edd4af6b93c731c9d95c195e1d856d6b10fd80f", + "templates/package/content/rubygems_dependencies.tmpl": "sha256:4984b3222958252d9a42ed98bd6a0d6e42ed6c293552c809c7f82c31b2430104", + "templates/package/content/swift.tmpl": "sha256:ee3852b8c7394fb8e941b192eee88a69bb9b909ce78f9fc9dcc505965f0e1704", + "templates/package/content/vagrant.tmpl": "sha256:d53f206513a905123c15df5d4852bfaf63f263087615c4f410da83b67c5341ed", + "templates/package/metadata/alpine.tmpl": "sha256:a1a605fa1c868afad3612e42e137507a266b1ba5e5c41e59286442eca0633bf3", + "templates/package/metadata/alt.tmpl": "sha256:2236ef221dfd7520a8c279a0ffbe52431b693fe0fdff95cf1089c62787882818", + "templates/package/metadata/arch.tmpl": "sha256:20d674ac70fe455a0487c9f588574600de0af0e9ac09b7dd264fc80ccc320741", + "templates/package/metadata/cargo.tmpl": "sha256:57494b6aba1c09c36a27eab70f86c431eb559fafd9bdb3ef9ac9e41626f7e9b5", + "templates/package/metadata/chef.tmpl": "sha256:2f77feb0eb48c22b003f5a81ba738e69bc52da5643f7d26e1ed3fae41c79ddb8", + "templates/package/metadata/composer.tmpl": "sha256:2295045ce4705ae38594fbb400c5c282d0daf83623e57de15335a6118c324684", + "templates/package/metadata/conan.tmpl": "sha256:95af05a3accd132f78f8c1dc1e75b725f1994b10e71c974d62dcd04daa895298", + "templates/package/metadata/conda.tmpl": "sha256:dc2448eea7d7aa873350b4a60e71b108d81bf5573ff0399294a072ac8d3d11a1", + "templates/package/metadata/container.tmpl": "sha256:d5116fa94887fbf0a0bff42cc241a4baf3319c5e33bc418513ca83f5f773b06f", + "templates/package/metadata/cran.tmpl": "sha256:01593c589719b095497fc68eb9105cf8a019413afec3a31c3b265b18e19ce71f", + "templates/package/metadata/debian.tmpl": "sha256:31945fb87d5ee708bd1d440fee825f3822f062027a30491d12c02f6d4eac6095", + "templates/package/metadata/generic.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/package/metadata/helm.tmpl": "sha256:7605796bd024790a153a56721f4cf58b5ebb7e55f553611c516ae2a1d0083224", + "templates/package/metadata/maven.tmpl": "sha256:a3141bedf947081f3429925429a12c2cb11373891d5bc2191f63078ad34ed2d5", + "templates/package/metadata/npm.tmpl": "sha256:00442f009de58188af621c05bd54b22c6f85c74471f01ea68676f1e4f467d40e", + "templates/package/metadata/nuget.tmpl": "sha256:8f319ca857b4c3400a3d6197c1dda6c87931c1c84fc7959189e9de46e5d03c84", + "templates/package/metadata/pub.tmpl": "sha256:7cbba58ff3689feb6999320af2905883bd5c25b84bcc9e6dd402ac0234c33167", + "templates/package/metadata/pypi.tmpl": "sha256:7de64f36722ee740839a47be7f1ebd522d2d784ed457df1423471c62e47090f7", + "templates/package/metadata/rpm.tmpl": "sha256:cbb4185298532a9acff9de76df99dddc9691a16ea970ef69dcb5e55671689389", + "templates/package/metadata/rubygems.tmpl": "sha256:affcfb43df2b08e5cfac1f9a926f40a61c3bee784c39da7600f0656158047113", + "templates/package/metadata/swift.tmpl": "sha256:ab01a92930792cc95abd6578cb2f3ba415215ac38988499459e7d333a44fd3b5", + "templates/package/metadata/vagrant.tmpl": "sha256:df0fbea1edc5668554b35a4323df4a746f6317e4054659974ba95b19602ba863", + "templates/package/settings.tmpl": "sha256:b7882fa05d32d88d33fe30c3df664624fb692216c0d3790c9b6fec82fb176ec1", + "templates/package/shared/cargo.tmpl": "sha256:57e80d29b9c7b819d71923e990dc95d827e6c58dd1f59bf1fcedd4b455cb2b44", + "templates/package/shared/cleanup_rules/edit.tmpl": "sha256:6dfcf1fd917262c8b457ff4df0c43995cc1751fd809d553eeabf7a72aaa0ed2a", + "templates/package/shared/cleanup_rules/list.tmpl": "sha256:f207c862f01a099077ba64c33b2563c44e0131f9439c9af579d2987b160fae51", + "templates/package/shared/cleanup_rules/preview.tmpl": "sha256:27ab9ccc2999ef22896130285e1bb280c4caddc51ecf80157ca1ebb315ded22e", + "templates/package/shared/list.tmpl": "sha256:32c8a7e461907debba8866a0a0da48d5e092fdb64794f863b6b62353838da737", + "templates/package/shared/versionlist.tmpl": "sha256:3b3bf98c8d1f3503158578fbe8085e0247eb75172501bd1345399b00b7f8a4bd", + "templates/package/view.tmpl": "sha256:01fd34fba237a6b9ca2899164eee8365902df1f43e1506e25d06ea86ec91aca2", + "templates/post-install.tmpl": "sha256:059e23b3e3dd5aa347f1f21c0f851862c3b45a094dcc7470373b26223b661fd1", + "templates/projects/list.tmpl": "sha256:2b8cdc2c62fb9de54d45e6cdd9b537f225186779a262ea24af3e48c61389969f", + "templates/projects/new.tmpl": "sha256:93ef138278276f3e6afb7807f0eacfa7885120e5413ce018acb3ded819f5d2d9", + "templates/projects/view.tmpl": "sha256:453afe11f0b735f44c80c02527f71bebd1b71ac73fee86fb55603c05f83e8c16", + "templates/repo/actions/dispatch.tmpl": "sha256:8defd0305f3e28c0e0d2a3524f0e71d27275027a4785a3e9981e96f0b4199f04", + "templates/repo/actions/list.tmpl": "sha256:688714b173fc398fccb512095f4d074a32dfc192bd05b4534cc8554964c1f00d", + "templates/repo/actions/no_workflows.tmpl": "sha256:9bd62cfd493cccf0c50d9fb8ec1bc6debab07c3a83729e2ed3be3589825de432", + "templates/repo/actions/runs_list.tmpl": "sha256:e390b6967dc307a4678025626a62b6178fc32083a2ea73414b26055176a28066", + "templates/repo/actions/status.tmpl": "sha256:ae4ea25863b49407a689341cc97b5b26f644d96d9ca84faedac672a3b8519f0b", + "templates/repo/actions/view.tmpl": "sha256:c0e8075bf483fb433fd0e411da8e1450d33e1b9c5ced665e85b5fe11a1d6b5d8", + "templates/repo/activity.tmpl": "sha256:60cce79800498066b21ec48a2dfc317ee593761d24945c20c481a8572806c9e3", + "templates/repo/admin_flags.tmpl": "sha256:6c60f2597d13731bf45faf28afc36f53ef793818e415373f476c20410f31e044", + "templates/repo/blame.tmpl": "sha256:b8968944d8d774013ac336b4469d3781feb7581a6cba3f221242a1fa605847ad", + "templates/repo/branch/list.tmpl": "sha256:9e0adc91bdc8006d626e96acec954ab6f9a1b798e4eb0e9a6a7645515d992d76", + "templates/repo/branch_dropdown.tmpl": "sha256:0d3a657b58fa06b24c459854d60cc49bf9a85b7fe0f167236321748d1c93cf41", + "templates/repo/cite/cite_buttons.tmpl": "sha256:602c7c5e072663695c09880e3a4f14f2d0c7ee20268e647105b8e96f183cbbbd", + "templates/repo/cite/cite_modal.tmpl": "sha256:ae8b5551a2e5e4fbd2e545e8544a12f4d766d6cbe111ce7cc20c18f7603c761c", + "templates/repo/clone_buttons.tmpl": "sha256:909f391790b470a2156f0747dd3cb77da5a6702d41f675a3bc4394ea6e30f0bf", + "templates/repo/clone_script.tmpl": "sha256:13c7bb20d2db5eabf727f221834b45edd0467f5a7e9a6e145a19be3ed5c00c16", + "templates/repo/code/recently_pushed_new_branches.tmpl": "sha256:699beddb248bf399bf43a5c13d7a366daaf12aafe57c007839af7605104ad7ba", + "templates/repo/code_frequency.tmpl": "sha256:4cd58689c8e4748de2b5938200abe297838f4ea980f8f98fa9469f4e32d2ec80", + "templates/repo/commit_load_branches_and_tags.tmpl": "sha256:e88da13a907079e9174cedb5e8de00a9ba144516a55c42c3980aa2c925124132", + "templates/repo/commit_page.tmpl": "sha256:9d0ddd42561e04ce46505bcd978a0a8377607300307a297aa41fcd5f1ffa2722", + "templates/repo/commit_status.tmpl": "sha256:0c38f506386805373670785deeeab0ef2b8eb3cde3cacbc9a645001a57d6783e", + "templates/repo/commit_statuses.tmpl": "sha256:3aaa196b566d833cbed1d681476709ee545f3867c398a4b2bc18c9f07766201f", + "templates/repo/commits.tmpl": "sha256:1af5aedadceda09de3d0590ce014cadb99575a1e1cbb6b0f75ec3f716e62f6fc", + "templates/repo/commits_list.tmpl": "sha256:bf41965a1857fd7af03789b6eb66d6cd16104e99e4d2c3301eb5f7e3da223a63", + "templates/repo/commits_list_small.tmpl": "sha256:a8018cce7a60e2dc46fe29da749b9385150cef6e3f1fad4c19ab99c3cbe683ee", + "templates/repo/commits_search_dropdown.tmpl": "sha256:3067b786fba41e18ddf7b329db84ebc1e9edbb72feb5fd453251670818a6afcd", + "templates/repo/commits_table.tmpl": "sha256:56ad1ca717dd6b4fa885f21c274dfc30f59bdf60ee7bbbe580caf426893f0762", + "templates/repo/contributors.tmpl": "sha256:6040a3a87965798b0da421f8e580b21237d9c8eb2770ad4c545ea4452dddcf32", + "templates/repo/create.tmpl": "sha256:83bffdef3709167841e0e866b1b53fa1ea3b48388a12de2bfe2f255d77dbf397", + "templates/repo/create_advanced.tmpl": "sha256:241546c58772fe0489acfb79758af9ca01c1c70a99fb98e38e340581d3456d60", + "templates/repo/create_basic.tmpl": "sha256:9deb7145c0ce7771ef3549a8fb22cd90e42d595bf19c7a93e774e4745c2fcaca", + "templates/repo/create_from_template.tmpl": "sha256:0045ba4bc0713b11d20377490af0214163a80f9c9668c6fa1afad189e61a160a", + "templates/repo/create_helper.tmpl": "sha256:1d6af42fe799040861bc5331cb0a9707eebf7ce390d06f542c50070145223769", + "templates/repo/create_init.tmpl": "sha256:573703fc84fe21c3bd86bc8c8edd86a4aaa19b709db77107da51c3ccaca28e8f", + "templates/repo/diff/blob_excerpt.tmpl": "sha256:c293cff666d30df783c22cdb2401c391345de2a2bd8e3da4b26310119455a91c", + "templates/repo/diff/box.tmpl": "sha256:350daa2b6724b049a03f08c5315ac28ef008e5ad205aad9281d958a4111cbcec", + "templates/repo/diff/comment_form.tmpl": "sha256:7060fbdcfcb82e7526b30ed60b0f45f670bdafbd0f298d3fc68106f711b65af9", + "templates/repo/diff/comment_form_datahandler.tmpl": "sha256:08a1f1ed00c43c25df00596c9e7816c607891f70683821523a2d0df0ff814c1c", + "templates/repo/diff/comments.tmpl": "sha256:efc79a8ae2973b4ba44d78cc04ee27fd8372ac5b2256e24378d188d2700c263f", + "templates/repo/diff/compare.tmpl": "sha256:0bcb1ac633ab09a3dddacf4d3d055c5315dd7e623483b753fbadb8b35c537613", + "templates/repo/diff/conversation.tmpl": "sha256:3bc52d90c1de78907ec172729ccdd8747e8cd0bb54315d6e3edf531ecd12dcde", + "templates/repo/diff/conversations.tmpl": "sha256:9818a040cb202626cd173a08fe46fc345164d78eabea07a9e68f4cb6b3f5de77", + "templates/repo/diff/csv_diff.tmpl": "sha256:2514fde9f4522551c2d368d3792afc7673b00c0d852f84ea4dfb6093cfb7796a", + "templates/repo/diff/escape_title.tmpl": "sha256:3315a23ce72b149b0d43ffa03ca2ab66c1012fe8eaf7286e2ad093da77711893", + "templates/repo/diff/image_diff.tmpl": "sha256:ecfd2dcf07e63fa6f75a8badaa26acb857818c232f1b35a31f538278c23d423b", + "templates/repo/diff/new_comment.tmpl": "sha256:0701962698fd284d98efda13326cc49372f3711acc20547fcb26d7c87584994d", + "templates/repo/diff/new_review.tmpl": "sha256:23de67fa67f2170c15d9bbcead8b9fd559cd35906f1369073c334c6daa354800", + "templates/repo/diff/options_dropdown.tmpl": "sha256:64df76812b9b6e28a4834a959109bc6e8eacec3d0262d06a24e805b7229a8e26", + "templates/repo/diff/section_code.tmpl": "sha256:f0c2b12797b538b3b465d3e7248bb327ddd66958c54536979eca2a02ca195466", + "templates/repo/diff/section_split.tmpl": "sha256:66fc2497ccb1c39080cd2ab9c2e247a86e4b37cff0c7ccf909a50079b69a0f40", + "templates/repo/diff/section_unified.tmpl": "sha256:36ca8f927ab4d222580a7e17ca7a75270226e562d89b2729447c126507f63708", + "templates/repo/diff/stats.tmpl": "sha256:3d93fa152fcf880fe15b0d0e1ab265cbe7657cd8fc3983418cf920e26a961661", + "templates/repo/diff/whitespace_dropdown.tmpl": "sha256:90aa383e514a2d587beeb95e89d54b4bc0d2c19bd8d9a086904ae9967e2c69a9", + "templates/repo/editor/cherry_pick.tmpl": "sha256:b30913fc97c23e60bec2e1ac3f62fa95580ec8a67539b4343e5061365f0de7b4", + "templates/repo/editor/commit_form.tmpl": "sha256:035bfa6d45ab6af8988c5695b74aa332e723f300d237ca8ff0c184a19793fb68", + "templates/repo/editor/delete.tmpl": "sha256:07821a1fba63d326ca755c819a716c1269a62dad0c48c75f54e2cddaae52fb25", + "templates/repo/editor/diff_preview.tmpl": "sha256:7ef87304e8dbe93be3ca45cd01184fc72c0eb4881d6bb7c5551c14a8524f3f7d", + "templates/repo/editor/edit.tmpl": "sha256:2863906e43cb5bd168cd2dba0f0bc02b215c187823d30dfd12a4758edf35030d", + "templates/repo/editor/patch.tmpl": "sha256:983939d2a775ee272e63a0da19973c662b3e77e89b4e16a14a68e4b9bc108839", + "templates/repo/editor/upload.tmpl": "sha256:c7a0aace80f3f7a78498dd499de13b493304be750219a5ccd71ca8c76bda0f04", + "templates/repo/empty.tmpl": "sha256:c25f3feb483755ef9c7ff5868e369f07f5678d35b4f1661d11c32e8e21000c77", + "templates/repo/file_info.tmpl": "sha256:49406ddcb57abe844ab51ad87e0332cbdc84c6bccaf212e7bb6c45b8d4014dc6", + "templates/repo/find/files.tmpl": "sha256:7b8bb3d319d0509f3d31df2bf27e5eac4ac8a0410c7f0a8c5ca74c2b30c319ec", + "templates/repo/flags.tmpl": "sha256:5123398864d9a56f0c278b74607b6aa648b35d10f5d698e128b182510686fc6a", + "templates/repo/forks.tmpl": "sha256:a519029ae63d7da5a6ad3e601bb6571b8f7fb9c79ea3a9a67b05713f32936e34", + "templates/repo/graph.tmpl": "sha256:fb938c350bbb9718ab6d0b289ff9156a4d184644581ec83a1ef01c8ebc6ddd0d", + "templates/repo/graph/commits.tmpl": "sha256:8eb0820ed73787e34b780d8938f569cde30791744914e9a4f109fa070d1d6077", + "templates/repo/graph/div.tmpl": "sha256:4dfe1cf9f4b7aa12d4ad35afd311e7b82d1631bd70e40ea7ad73db3db0fc83fb", + "templates/repo/graph/svgcontainer.tmpl": "sha256:e3e7c91feccb047b44842d04810f6eb900f211f7b129c1ae3f351316f31d50c6", + "templates/repo/header.tmpl": "sha256:d3c2e97044acef0feb401a8cbf637090c658c10665a0b01e001b0fa9fa1b5b1f", + "templates/repo/header_fork.tmpl": "sha256:a8d98a90b7754466a028cbf67f434128cc501860edbe8973f717e7eead287d2d", + "templates/repo/home.tmpl": "sha256:5d6c048a09b23ef8eb3b0388588c23f8d17d7e8092fb2f4e2a25a00f8f4dd9cd", + "templates/repo/icon.tmpl": "sha256:a0fc4ab1ea745a0518d4d0f1c9286feb40504eadbcd2b965196bb8037ccf6aff", + "templates/repo/issue/card.tmpl": "sha256:2dd85fc099e21a78f54d49b09f44b060ee28efcda9e6e210d27558dd015e777b", + "templates/repo/issue/choose.tmpl": "sha256:4cd7d32ae9abd0bcb831f5f0b340020cf0034d5d8ea18c338905a30a973fd825", + "templates/repo/issue/comment_tab.tmpl": "sha256:279e2c8be313214ce0a7ea2e05a2af5a99fac780d498580f747257c2051e46de", + "templates/repo/issue/fields/checkboxes.tmpl": "sha256:90f9549916ead1628765c620d2e1127add599a6c5102d6d1fff53985b91c0bca", + "templates/repo/issue/fields/dropdown.tmpl": "sha256:0ff7417092fef859347759ce489491458d752a96de451cf0bd11ad1f92f781cc", + "templates/repo/issue/fields/header.tmpl": "sha256:5b0617ff13edd0ad03be30cf82deffdce41afbd47bf7b9e275b8e98553efdfa6", + "templates/repo/issue/fields/input.tmpl": "sha256:005e88a331dd41c03e34d30e53d9a6b3d6154d42fd6e7ec48ed9aa8a5710de08", + "templates/repo/issue/fields/markdown.tmpl": "sha256:b483d0ef2d73316f6a07c7fba40d9062e3f6a96ad6a611e9f07b8acb6b880ef3", + "templates/repo/issue/fields/textarea.tmpl": "sha256:1b0f3b42693a032899b028637616b21d6b69a2606c9c63db3d6abfe01f418dc2", + "templates/repo/issue/filter_actions.tmpl": "sha256:68c9bd250e7826ee540d16fd302e9d74b4f3b87ca4c6eb39ff978772a7c59ff4", + "templates/repo/issue/filter_list.tmpl": "sha256:cdf2abb2e68b5e587d9f3a80ee58324e0725715ab582ec43f00e4363428bd921", + "templates/repo/issue/filters.tmpl": "sha256:bb1f8ae02931290aa2da02fbcd6628078160f85b0432cc21bbb54c19258c312a", + "templates/repo/issue/label_precolors.tmpl": "sha256:7a03c712c4ee0adc9027aa608e90d0fc7e98853f2bb2eef0ae46fa00796b0a66", + "templates/repo/issue/labels.tmpl": "sha256:629efcdbd8baf254c5608de6f298e7a8387d64686bac39835eb283c6bc34b478", + "templates/repo/issue/labels/edit_delete_label.tmpl": "sha256:2081aba424bbaa3e4c5331fe9dc7f1dbdbb86d2313b83c58911c877b745ae4f2", + "templates/repo/issue/labels/label.tmpl": "sha256:0063d4395272df4916f7806ca03ec8c7b88ef2edae38c29990ceb88dd72d68a8", + "templates/repo/issue/labels/label_archived.tmpl": "sha256:3743e4054612bf08e571cc79dfe26a92cf0679a19a9a64651eedda925d24cbda", + "templates/repo/issue/labels/label_list.tmpl": "sha256:dd992952b2384a8f8906ade9affbece97f56612c7b0a662012c8b1e67507a356", + "templates/repo/issue/labels/label_load_template.tmpl": "sha256:0f8ef81ca502901ad7bd0a3d19069a71e7da5359f9d7fb0adad4095dca0e30cd", + "templates/repo/issue/labels/label_new.tmpl": "sha256:b28a0288a99da41bcf7e20e56f88b3df43f4a426ad0e31bf83a6ac0d35e73598", + "templates/repo/issue/labels/labels_selector_field.tmpl": "sha256:ccac8850c22528d579108613a1a430ec13d97dc9b921a8f13acee3bea5817e1b", + "templates/repo/issue/labels/labels_sidebar.tmpl": "sha256:eec4daff807fe90793fc583dce0c136b8c642afe4268b366dfb5bd68fdfe53c0", + "templates/repo/issue/list.tmpl": "sha256:6183c65967be73a2fa70003661cb2d5aa3b3eff2c70fda8129c86c3e1b77d3ae", + "templates/repo/issue/milestone/filter_list.tmpl": "sha256:01cbea12a477d34557fe16574b1fd3048fd01b9e12db68840bfe5bd4f72306cd", + "templates/repo/issue/milestone/select_menu.tmpl": "sha256:089d5947652ec0edbdced3a2fae00696cefe94dfd24d86766bb2c5bc6fab74a8", + "templates/repo/issue/milestone_issues.tmpl": "sha256:cdad825ee1c2d611e0e3d024d8654e818e1f3039dec9dcf4751df33279705cff", + "templates/repo/issue/milestone_new.tmpl": "sha256:2889a2eb4a26504d7aeef129ac406954fe5327ce468904b1fda6dadafd64f5bc", + "templates/repo/issue/milestones.tmpl": "sha256:200e3b69b79e4a2ea06b0e79d07c2534be8ed5d5e3aea0f76f28419364c46528", + "templates/repo/issue/navbar.tmpl": "sha256:abbd69dbc83e76a85b43b8fe587ccc6a521e2d6eb0cbc8a7c3cb01c2da4d1dad", + "templates/repo/issue/new.tmpl": "sha256:2bbb56cd40bc21e81d053f45a16fc06c7d344bf7db369917e18f1962374c00b9", + "templates/repo/issue/new_form.tmpl": "sha256:5706fcc728170cc93fbb116a6a74a653f16c6a1711f480d4d9e67ccf812a6ca6", + "templates/repo/issue/openclose.tmpl": "sha256:0bfb23eee0079995ef7140165fe050af5778526239a18ac0c32315b89bf805d0", + "templates/repo/issue/search.tmpl": "sha256:307c7b2c709a41d091cbc8c18e8323f9eb0bfcc1392b7640efdcda7fbd27ece2", + "templates/repo/issue/view.tmpl": "sha256:2eec9c9a17d0b7f687993e35900f594d1fd7983c1feeac87ef97f27a7b55ec7d", + "templates/repo/issue/view_content.tmpl": "sha256:be28fddee5d2f78622bec8ed945337e4810ff85f6f2e8ffdc647f22e91ca0e00", + "templates/repo/issue/view_content/add_reaction.tmpl": "sha256:cdf1c73caee934085ea25d568aeff7f78a8343f4cd1cb74766849f615d8e1433", + "templates/repo/issue/view_content/attachments.tmpl": "sha256:0b9b9ce0f0e4a1323900f0f9276ec61596e3c16ea6cb58a2edb859917f8ce7d6", + "templates/repo/issue/view_content/comments.tmpl": "sha256:93df334223c245efec32b1795f9ea88b7b3c9f8f6e4d5417862f672633ad8ce4", + "templates/repo/issue/view_content/comments_authorlink.tmpl": "sha256:bc5d3a5a56563c43cb1d2ef82376793bd3333a8f87d443181533f47e5e20989b", + "templates/repo/issue/view_content/comments_delete_time.tmpl": "sha256:04fd452197ae0fe5732d5f5898a4d7751ad1bbc2925feff18f699f91b468c1dc", + "templates/repo/issue/view_content/context_menu.tmpl": "sha256:e39e4454eac554ad5dcea8c567f2e4cd5d48c56dac8c88baf3353525ebad2842", + "templates/repo/issue/view_content/conversation.tmpl": "sha256:7c75c658fa5ee1e53a173440b685c2355088b1fa409fd5a776d38fd44ebecea9", + "templates/repo/issue/view_content/pull.tmpl": "sha256:f06ab8239a51192c9d44263075d550eb2a2bcbd5bbd5dae182e468e40a97cb26", + "templates/repo/issue/view_content/pull_merge_instruction.tmpl": "sha256:6c8bc37f1c79c0b0583ff36e9c649f16e963d89c611f050e82a751d072e5eb5d", + "templates/repo/issue/view_content/reactions.tmpl": "sha256:d7ead307097759282abe27a3eba2285c8d1a98719bc8730c4f8110188c8de149", + "templates/repo/issue/view_content/reference_issue_dialog.tmpl": "sha256:569bbb5fbe6013265603d6b2696c592bc48fb62e42c07d8620580e20c140e3c2", + "templates/repo/issue/view_content/show_role.tmpl": "sha256:dd97809e4ab97440c7eb4aa749eb589618ed3b8e143f85e160675b9c1c981bc6", + "templates/repo/issue/view_content/sidebar.tmpl": "sha256:413218c1430b3a6b6327c502d8cd740f3efe535c50e12c5033177288eebd2231", + "templates/repo/issue/view_content/sidebar/actions.tmpl": "sha256:8c15f768c4c53538df0eeaa72ec044e573477e7d545051e2c5ce00b07f8fbe0b", + "templates/repo/issue/view_content/sidebar/assignees.tmpl": "sha256:a39a50962e14ef2434cec6f4956ed51973d41f1ba45c9db9a8b86f49e298a158", + "templates/repo/issue/view_content/sidebar/branch_selector_field.tmpl": "sha256:c2e65dd2da0ea33f8bc71cdad03b844553def002276a271da9c6f652fb9e4a7e", + "templates/repo/issue/view_content/sidebar/dependencies.tmpl": "sha256:523faee59f4844b6c9df0e2d28e6aba4a9a3a4286d63892b8fdd75549ee0dbdb", + "templates/repo/issue/view_content/sidebar/due_deadline.tmpl": "sha256:d37a8e5028c3227eee328ad083aa0c2e440e72924be7f02681791ba8f1055e18", + "templates/repo/issue/view_content/sidebar/milestones.tmpl": "sha256:a5dab6a55bbf21528ad668bc63d5ca6868a9dbc9fa326fb99d602c848915e1c9", + "templates/repo/issue/view_content/sidebar/participants.tmpl": "sha256:0782615b24cd9dbaabebc16a3d87a594ddac591da21c094931bb16ff299b55b8", + "templates/repo/issue/view_content/sidebar/projects.tmpl": "sha256:7af9b2590bcf3b2855ad81bd244d26a77cb27a5d578793919027c856f9a8a65c", + "templates/repo/issue/view_content/sidebar/pull_maintainer_edits.tmpl": "sha256:17a3f7a537268655db390e37ea4712b87ec08b6b6694627828d6923ae62c3d99", + "templates/repo/issue/view_content/sidebar/pull_review.tmpl": "sha256:e5b11e86d92c12ca2b3bf7c5295642b980b5dec94464e04b74f102b6d85eb0a7", + "templates/repo/issue/view_content/sidebar/pull_reviewers.tmpl": "sha256:731094213625689c92e4a0565e51a0d252c2ee423916b36c9ac457d76e242663", + "templates/repo/issue/view_content/sidebar/pull_wip.tmpl": "sha256:204617cf47911e14302fc8e7716e47b6424f2eebf056f2c18c996efddb4eabcc", + "templates/repo/issue/view_content/sidebar/reference.tmpl": "sha256:2c37511cda7f31fbf32910df5bbd437bccec49e5784aca00f17bfc09df8372b7", + "templates/repo/issue/view_content/sidebar/timetracking.tmpl": "sha256:a25bb7f6031bd19d2aff9b9ba8d9cfffa1f3b6a959f92c4601a79cab2295595d", + "templates/repo/issue/view_content/sidebar/watch.tmpl": "sha256:fbfa92ed882b2804616b131f351d270283176041e20a675241e5a96d944c854c", + "templates/repo/issue/view_content/sidebar/watching.tmpl": "sha256:867c46058480fc1aeaf138e66a32819274c173cf43f7a95f871b71e7d90da8f5", + "templates/repo/issue/view_content/update_branch_by_merge.tmpl": "sha256:d636e6547ce88c25f19094e73ecfbdf3061be1a1a42cbedc1ae11eac278cd1d2", + "templates/repo/issue/view_title.tmpl": "sha256:2a4b023152b7fc1e8c6c4a033d1dbc79a2a7be597170ab6103ab2f84a46e28ca", + "templates/repo/latest_commit.tmpl": "sha256:344b10efb7e265ca84f2b9fec9c139623369f682343ace9f40d3bbba1073ab99", + "templates/repo/migrate/codebase.tmpl": "sha256:3b5a01bd90a8a813c20bc88a2d36a7783bd985cb43fed2fabebb29d4081c3e02", + "templates/repo/migrate/forgejo.tmpl": "sha256:e68aedb7a4cd41de14f7068fba3b9fae185808a57f2ca437dd58fa15bffd7f90", + "templates/repo/migrate/git.tmpl": "sha256:848ad307b5bd2ea513675a41b73ffd65179ece874791226d4cbe211734393822", + "templates/repo/migrate/gitbucket.tmpl": "sha256:b1ce372da094df4ff8aa7fc65ce0b6ebe8a55e0c9b5ecfa30f28b2c0740c5b74", + "templates/repo/migrate/gitea.tmpl": "sha256:e42c3bb67e3a2a7795446093e45ae13f06e91f922ac33906f502b9b352b11810", + "templates/repo/migrate/github.tmpl": "sha256:c8007bd01186729851b2aafc5dfe632c6f740d52af80cc9494108382b37d0242", + "templates/repo/migrate/gitlab.tmpl": "sha256:a2c4494fc8a4637f35acdb632a6650a924dda28865709bc6220f38949542f012", + "templates/repo/migrate/gogs.tmpl": "sha256:9f1ae3187a8e605aa49d224e85c576b350bf6ecb69f82aabf69868000ee8c645", + "templates/repo/migrate/helper.tmpl": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "templates/repo/migrate/migrate.tmpl": "sha256:a9fedd31de3bcde8c5b9dce957b5db9171efac5a3a86063c4a4dab1d3c06b014", + "templates/repo/migrate/migrating.tmpl": "sha256:3c9ecf326b1e45cad92e7cbe65310918eb6c148d431cbb9bfe7789155e1967a6", + "templates/repo/migrate/onedev.tmpl": "sha256:b3979f702fff517b5601918d44ad078b2a1221c545d78f734146a2ba6a788b77", + "templates/repo/migrate/options.tmpl": "sha256:d52231534e2f6b13ecc0f7f3f4ba420fbc9d10685b453baf7c04cf531c58a61c", + "templates/repo/navbar.tmpl": "sha256:a8fc8489baf173801eafe62562df5b58fe4f6db279af182481c4f3c73cd68282", + "templates/repo/packages.tmpl": "sha256:efe6aa148ab463f0564b86a9c610859fe50d1b42f32ce03ada314b5371727df1", + "templates/repo/projects/list.tmpl": "sha256:393a0457150580cab1c757a361a7e0f999203c23ba94f017139f56518ef01fc7", + "templates/repo/projects/new.tmpl": "sha256:e41a6fc08613492002ef37ea4c46123aa64dd77a87193144df9921bf2626663f", + "templates/repo/projects/view.tmpl": "sha256:3cc59e16c737f9eed11f617f1af8c2059b03f4611c12654e05dc55635cf88243", + "templates/repo/pulls/commits.tmpl": "sha256:f62f0aaddf1d8494dece8ac4f72873fbb6fffbf282038175d58d9ccbca4b51e0", + "templates/repo/pulls/files.tmpl": "sha256:ed8b7dcf779d23b40b24bf8694c6401814ba308a6ad44b5403b608387c18c675", + "templates/repo/pulls/fork.tmpl": "sha256:0f7adcc374c4e3be8a77f7879e75915cda4bb2bca55e92d883916517e52e8709", + "templates/repo/pulls/status.tmpl": "sha256:79967aefe17397033c7318d05b451afdb02dcf1ef039415ccbf923d324668dc6", + "templates/repo/pulls/tab_menu.tmpl": "sha256:13e1fe79036d6df46fe2e296a383c30fffe296f7b420435fc90e005657095a64", + "templates/repo/pulse.tmpl": "sha256:2e71501c1f15dcd33184d4fb1386264d877a4ea0396799928ba595cc9a1af5ab", + "templates/repo/recent_commits.tmpl": "sha256:7e9642ced6f4cdec52fbc41b0bcae64c7337c352b2d1b79703a376ca2e250c77", + "templates/repo/release/list.tmpl": "sha256:7e451572eb94062a2b06a38c649b36fe4dbec989f6c9a99b2795d0355ab0646b", + "templates/repo/release/new.tmpl": "sha256:f597c6284258020ca72f20de00415ec12354244990c9e657f39542da74e9c252", + "templates/repo/release_tag_header.tmpl": "sha256:6a268edc4ae1427a0c1524cec3ffcad5c8874538c5abc7e1ed28d667241cd29c", + "templates/repo/search.tmpl": "sha256:16aa2ff93305811a67b7f3ac76c8f7acf6dbf8a0bafe8cdef7356e1b06a035c5", + "templates/repo/search_name.tmpl": "sha256:1e3fb2227e651d7472d929b2f33f14707c1282586da35a5c9c9eebc847549285", + "templates/repo/settings/actions.tmpl": "sha256:c3d3cba18e10f5d813970415b67270b8612829d936d2c61d9d74d03ea69ff2a0", + "templates/repo/settings/branches.tmpl": "sha256:7e3ae72d6403772f92ac02087725e7ad12cb432d9822477aee1be749fd0cf1b2", + "templates/repo/settings/collaboration.tmpl": "sha256:f4288c30fe0ec82f16ad501427e8e9ac15136c05d2435a97b0e5d94813b1d3ab", + "templates/repo/settings/deploy_keys.tmpl": "sha256:b607c6cf5f8e04416e283ef2b0eea7a71821ab7f36cfdfc0602f5b788d36b821", + "templates/repo/settings/githook_edit.tmpl": "sha256:2f5d744b6581991a9cab62b9366da464c139491c966d0f48bcf70047d515d947", + "templates/repo/settings/githooks.tmpl": "sha256:54fb05e758cdf71321f319217d835dd3731d04d2a79c0c277d1bef1729e35305", + "templates/repo/settings/layout_footer.tmpl": "sha256:47769b3c183e1433714df4b2cdcae0649a1c58e8caab2d3fbb681cf356d68878", + "templates/repo/settings/layout_head.tmpl": "sha256:81ed7113556430efd15cb2ca12b932ec46a481ec6eb4976d3bbe941e236facb2", + "templates/repo/settings/lfs.tmpl": "sha256:1691fd5e9a7ca968c4861613e4d49361f5d38d2764b3841ad185ad526dc53c2d", + "templates/repo/settings/lfs_file.tmpl": "sha256:df2464178aa42320252eb274a3c66b5dc21cbcf9ab4a201a55243c022dc73a2e", + "templates/repo/settings/lfs_file_find.tmpl": "sha256:9b111402b45515fcb20413e97be86b3185ba670005aeba8f4c172ef200ebf6a3", + "templates/repo/settings/lfs_locks.tmpl": "sha256:105cef9da5dfc435c6dfa8c71f3c1097a915074211bec2fee28e76205f2efbc0", + "templates/repo/settings/lfs_pointers.tmpl": "sha256:7d5128fbd20fabd67f444c3faf75a7867484eaddb4dc05a19e5e004ab2a7f71f", + "templates/repo/settings/navbar.tmpl": "sha256:b743a3135c1969c22386c921855ee8211f2fd0b98f615cd1410e67cbff39c12e", + "templates/repo/settings/options.tmpl": "sha256:ebe70a7472f891302941fc8ec037df6dc4f36ad7141f44836b17cdd5dddeb8a7", + "templates/repo/settings/protected_branch.tmpl": "sha256:60b9b9a85e76f2167f4e3915e6fa1dd9d687868b446cddba1a43c129f232e407", + "templates/repo/settings/push_mirror_sync_modal.tmpl": "sha256:bd09dd337dd5d3fd20b75061bcc91965bf5b156c48a75dc9adf287a65f9921f1", + "templates/repo/settings/runner_edit.tmpl": "sha256:33000c8a309b2138b9ebf951b6cb0d5ef940810f3c3cd619cb644bb3ef15f179", + "templates/repo/settings/secrets.tmpl": "sha256:bfda544f16c80cb6472eb3332f7b55ef1653a6a8a22dfbac71b2d89dd83f65d9", + "templates/repo/settings/tags.tmpl": "sha256:2b1b57a931a7f6ced2429e3ae89e8335ceb2fc906d7f96b36c5b7ad7235c274a", + "templates/repo/settings/units.tmpl": "sha256:63b20e613db8043cdb893ef2ea4f9457f1de66d7b02cb20d642322ba34149c8e", + "templates/repo/settings/units/issues.tmpl": "sha256:fcc2493196eaab252a66c715875e921434048b56a25218ee194134d0fb39ab2f", + "templates/repo/settings/units/overview.tmpl": "sha256:5784a025dffe9dc3edfc0de7c8b8db1267f485dc3de5c669a95b8f3a1c31b91e", + "templates/repo/settings/units/pulls.tmpl": "sha256:6336584b0cd39ecddd237e14f2516c581c365f57dae6fd258ba2a1713d60aea5", + "templates/repo/settings/units/wiki.tmpl": "sha256:32fff2388f23334aece509b6d516fe475c16797989db7f1405407e5be0c85876", + "templates/repo/settings/webhook/base.tmpl": "sha256:d210d3e6c5802fbf3ecefdc7a329e788ad7fae0fd04813874f10d236fd80340e", + "templates/repo/settings/webhook/base_list.tmpl": "sha256:c1c81ed25b0f6f2fc00563161416a77ad7daf54c7ed8f7e03bb232015dd8c065", + "templates/repo/settings/webhook/delete_modal.tmpl": "sha256:93611abab4fdcf78ff0c41ec76d590a39001ab0dfc16e6f42a8fc3c4d4a52202", + "templates/repo/settings/webhook/history.tmpl": "sha256:486b0f27b8b574629bbbdeb6149613b6e9d0291b3f2c457679b00923f658fd09", + "templates/repo/settings/webhook/link_menu.tmpl": "sha256:506dc008f6552927710da5ee2ee41d0d9c1099072b8ebcaf4c39ad4cfff2d1e6", + "templates/repo/settings/webhook/list.tmpl": "sha256:b51224a331473bca3dc609bf7c73bc91b02e08dfaf2c1a385f115cd1b942036b", + "templates/repo/settings/webhook/new.tmpl": "sha256:56f40561c4f892a3c468e8e3820a58564a19286f226e2832d2011e3bccf611a8", + "templates/repo/shabox_badge.tmpl": "sha256:2fb34603d759929d3464ce60c113071bd3ffaee2064f382f5bbcbde5fbfc07f0", + "templates/repo/star_unstar.tmpl": "sha256:6c4658265acaf71be6733900706c8a694f4d983eb93d8f7c21ce047a5cbf55ce", + "templates/repo/sub_menu.tmpl": "sha256:4432a0d5da0de11419f41be420d1a9fd0f7313ac3246ead1191911d5fdd68d02", + "templates/repo/tag/list.tmpl": "sha256:df8f868cb6df4d540158922e6ae46bba7f6dd9cf3372459b9bcc399884ffe64e", + "templates/repo/tag/verification_box.tmpl": "sha256:a12120d926150f3536f557cf4336c0f2c5f11390f26e103020d54813dabbdd8b", + "templates/repo/tag/verification_line.tmpl": "sha256:db84e77669139569a37d3c1709486a26ce3c2c9675995cd319f9416391be13b2", + "templates/repo/unicode_escape_prompt.tmpl": "sha256:890970e9a81bfc5e3877832c7cb03782bf207d74bb8051645c3ed42ee2e01bae", + "templates/repo/upload.tmpl": "sha256:956b9a629f948a05ed99d20979f6d19be00c832b4f52c4405366f4dbd720d20e", + "templates/repo/user_cards.tmpl": "sha256:52108277d02b393176fad0c2061da9def3fe4d258d419993eee379d87d360b9e", + "templates/repo/view_file.tmpl": "sha256:217d900e34dcf4a7acd650b547942fc4bd75a7554302400f12c775f0add00ead", + "templates/repo/view_list.tmpl": "sha256:58d100b33ba3d7470099b2c1b9786f4a40f7dd0ab1c6f6768b5e14893b12864c", + "templates/repo/watch_unwatch.tmpl": "sha256:f4cd09530b93cafee7a4bb9c85ab29dc80523d05eada09b7e5947271cfec305c", + "templates/repo/watchers.tmpl": "sha256:19350d7226014e63bfa7b9d12e48128e1f3d84ee8686f7603bc56e2c0f0b53e8", + "templates/repo/wiki/new.tmpl": "sha256:cba3c5c0c5fa52fce509be938c4ebfee2a694c9ee11ad61af1f6bd884064a342", + "templates/repo/wiki/pages.tmpl": "sha256:f51549e3a26f4047022a0ea036feba2facc8f6dde285eb691874a7a7ce2c5c30", + "templates/repo/wiki/revision.tmpl": "sha256:38abad81879dfefb53a7cd6e0ee8808e12da144a5599d53b110b5932d606f6c8", + "templates/repo/wiki/search.tmpl": "sha256:a1bc2b8774b415885f65ae031f74daddc1b003c48b8226528d8ad2d5044c4007", + "templates/repo/wiki/start.tmpl": "sha256:df9bba8dd8bd9a8b7f24ac4fe958ea15a37cfc8aba93a9d8784953f1a7b58877", + "templates/repo/wiki/view.tmpl": "sha256:8e0f18b33be42b4182e438ed7abcc62674c52b9e41486dcc5c6d6f3dd948e179", + "templates/shared/actions/runner_edit.tmpl": "sha256:8ef136cf6b2bb83c13d7987430646557e5ace82c0fbd3da7aff2017b6a38e8ed", + "templates/shared/actions/runner_list.tmpl": "sha256:e94afa4b1ef95af7e12064f655496db12257253a20116ed9ac0c171acb3075af", + "templates/shared/blocked_users_list.tmpl": "sha256:153a3b6a72454494491e87a38d9c1d12f96d33f3cd9f4b92e348f47e0b2d3ae3", + "templates/shared/combomarkdowneditor.tmpl": "sha256:519529fb3fd36336b16cd55d82e9bb33fa38478f1775667eb78a5fff3dae6056", + "templates/shared/issueicon.tmpl": "sha256:aee925ab8cca5095afc41695526dacbfebec51edd314b2f630162ae271629d75", + "templates/shared/issuelist.tmpl": "sha256:5dbf0f4dc26ccc7c660081114b218e0d35d3d6f4c7675d70a5704583c09be3f1", + "templates/shared/label_filter.tmpl": "sha256:9682918b72ad9e22e7590c0bfaccd833327836854860c837a7c63f6e1aacc5da", + "templates/shared/quota_overview.tmpl": "sha256:f14c4a55bf41590c55a2306d9e06e6b04af06cad4cbae282dfe80438da899b98", + "templates/shared/repo_search.tmpl": "sha256:14f200efa8b10c217f82a42df838767581d8093ba9a9eda6ce87bbcb2429037c", + "templates/shared/search/button.tmpl": "sha256:2a25b71cf4a20c1f7ee47edd86c0f61e29f9ae8265bf651ee57ed41abbdded4e", + "templates/shared/search/code/results.tmpl": "sha256:6be714d62fb9b2b848e38e8a43e04224c8c5c739c53e3d3cce1abac5e548bafa", + "templates/shared/search/code/search.tmpl": "sha256:2742e1baaa3616b621ef9b4144777b5c23f8ac5d53dd22af7f35e278c7684ffe", + "templates/shared/search/combo.tmpl": "sha256:526169b5df84e45bcfde637d39afb4797430dfd47b8dfed0b3ce6c8c7a7039a3", + "templates/shared/search/combo_multi.tmpl": "sha256:532f45871b787501f32517a659300d2b44b8ac9f4a7b0aea4d76d386975d1eff", + "templates/shared/search/input.tmpl": "sha256:74afc638e6e21e551943534dd1e68d33333c996ac5f848e3c2f17f47db0179b6", + "templates/shared/searchbottom.tmpl": "sha256:206aeb15d391999c02128a06c15d8375a4d178a592442c4f8a2bd97d1e232211", + "templates/shared/searchfile.tmpl": "sha256:d299ad7fc0a4b85ee780ce7074c72cded5ab5a626957b49dcd9a9ca15af34893", + "templates/shared/secrets/add_list.tmpl": "sha256:4cb3b419fd35415518fd2eccaaf7da34347af23799915397539e74ef52aeb43b", + "templates/shared/user/authorlink.tmpl": "sha256:c3b1fc95e9418be7183ebab2c0a1f6da1b4a0157052271f8e9d8d0971dab9e3e", + "templates/shared/user/avatarlink.tmpl": "sha256:c38bec2cff8a986ad6f74b4ffc3c501a528a37a0e62aeb1cad4de75d5fbaf160", + "templates/shared/user/mention_highlight.tmpl": "sha256:4e4cf5c301c78e8d5e65af9d3e54cbbb031e9618961ad91dc679cdba3544bdc5", + "templates/shared/user/name.tmpl": "sha256:b3a2b9c36f8a10dd36abb49548d7d98a0691d9fcf673e323df246c4fec7f81ad", + "templates/shared/user/namelink.tmpl": "sha256:3805e398f1cd33dab693b5c1c370e9a22c9163651411e82a74222079f85e2cba", + "templates/shared/user/org_profile_avatar.tmpl": "sha256:8166780ce77a055fb1488de2436dd56278545ba6f059d4612c198ab0df652255", + "templates/shared/user/profile_big_avatar.tmpl": "sha256:0a916eca3e0b8873bc8054cecead62b63b45f6567f067a17e3bb20195304c3ad", + "templates/shared/variables/variable_list.tmpl": "sha256:29957a3c774843312ffa75972fb6a28dd94525351eae9a9a15fecfad0acc5c0d", + "templates/shared/webhook/icon.tmpl": "sha256:f66ea55e0c9a30b30aa6f29c8b7a67b198f208e2407a9589da8b74f5cd3fd88f", + "templates/status/404.tmpl": "sha256:e13910cb228ef495ab7a64317d15fdd69b6322bba07aa47a12f63781a73a91d1", + "templates/status/413.tmpl": "sha256:f1a38d35f9c1259369e8bcb4aa5f8618a43cf46c69c95f127b525a875bab0b19", + "templates/status/500.tmpl": "sha256:8676f2725d22b3ec9038477c1a4db6f30aa873a18da9a88b2fad98e37ad81dd8", + "templates/swagger/forgejo-ui.tmpl": "sha256:92eed9f73f342e0748e1f223077142fd4dc0ffa9497d1ac524ed43bf8f9f352d", + "templates/swagger/ui.tmpl": "sha256:1bd98907ee1121b05cc23ed4e921a5d8a4659b28707de8fe626831a760672e7f", + "templates/swagger/v1_json.tmpl": "sha256:8eadb817dcdc477e4790587f4243c24d007b576f4d8e8344e4d738df8a83a047", + "templates/user/auth/activate.tmpl": "sha256:549d8153d90947d44ba1941b443dd159a4a0487479f81d66339e066cd499f58a", + "templates/user/auth/captcha.tmpl": "sha256:7013264ceb895d1328193458111be23cbb1e9c4a7d449fdbb5bb51fbefcd30e4", + "templates/user/auth/change_passwd.tmpl": "sha256:3bacab8652ba773ce17bf2d89eb9e9135609940bfdc77b891ba3e8157dd10fc3", + "templates/user/auth/change_passwd_inner.tmpl": "sha256:1c6a27ea0a02e30364be5b04831eea32ac85cdf5d95a75194f4c64b7c0b2a0bb", + "templates/user/auth/finalize_openid.tmpl": "sha256:ebff4d6cb1a6659e7416bc495682899b1732e60ef0d99565f50e1aef3f906563", + "templates/user/auth/forgot_passwd.tmpl": "sha256:5dc129dc0957dc6c44dbfe1c0a560e5184d754216ff19bc6cfc8a39ad9eb4c92", + "templates/user/auth/grant.tmpl": "sha256:5b37deb2e69c381a6b89765491c8c27c37ebc5e34d769a29f6cf1def8bd674b5", + "templates/user/auth/grant_error.tmpl": "sha256:0b5bc97171fcbb17d5845fe746740ccc7d4d5d31769c50790b3f16f64dffb775", + "templates/user/auth/link_account.tmpl": "sha256:b6e0a3fa0a8863728d12d4ab423058e5afd48e40f6b59152fed3be56c3ef9382", + "templates/user/auth/oauth_container.tmpl": "sha256:c163c648113904b362c42087cef86dbefe5aa52a560708de945657477c7f0c9a", + "templates/user/auth/oidc_wellknown.tmpl": "sha256:5ef31906f8dcce025f4c88ccccbc809f149872f1fac59e378fb1cad2558ffc0a", + "templates/user/auth/prohibit_login.tmpl": "sha256:4b95c0a51eddf76bfc15c42823dbc566188f0c8ff9b6f436cc83b06cc6621e5a", + "templates/user/auth/reset_passwd.tmpl": "sha256:13489a6bd90c051fabdfdcc9d01867f1eb6a51363416b4595eb2b76656cacafc", + "templates/user/auth/signin.tmpl": "sha256:b19374666dc4bff231aa49d60d2459cfebf565028edf67b439b3d093fc3a7547", + "templates/user/auth/signin_inner.tmpl": "sha256:e92e1a10b14362d56e47e98e7f1374ec12cc332de5898842b20a2bcdf93f284a", + "templates/user/auth/signin_openid.tmpl": "sha256:5728c2a73b476138c8e2fdb21869cf3167a0d6112c755c9a54872bcad6546593", + "templates/user/auth/signup.tmpl": "sha256:794d0f90f6a389616f8d0a1bbbf4a0f6c60cbc1727da5e26ff2ac969506e44ba", + "templates/user/auth/signup_inner.tmpl": "sha256:b777ed7ec11d3fb4263e5e12cfa3de5e02275dde479dcb81a36e204686836429", + "templates/user/auth/signup_openid_connect.tmpl": "sha256:83aec8bcbf797e70ac8423706776a8cb5c5ebff68413be89de1da0c23bb190cc", + "templates/user/auth/signup_openid_navbar.tmpl": "sha256:bc707b58366cead7369069f8a07a371c2e4279842d4e10477572529d04faa03f", + "templates/user/auth/signup_openid_register.tmpl": "sha256:0a19d724cde63f9d881c24aa1df2c8285cd288fd151b46af05db5ce5d1626112", + "templates/user/auth/twofa.tmpl": "sha256:c61b271dc21de6fb703105276208ff206f8c6d076b8f4a27feb5618b2a51b8f4", + "templates/user/auth/twofa_scratch.tmpl": "sha256:f30fd775b8a25e71e36d11db4aa5ff543e4737c571d1e7155ce7bd0482292779", + "templates/user/auth/webauthn.tmpl": "sha256:469c1cb9ef178590b0dcdc6240e3e6ead85a1b89efe76cfd454242578236bea9", + "templates/user/auth/webauthn_error.tmpl": "sha256:ff32c3143a7087dea28de9b29329d051d9db929eb2e32f4e5e3f236305b2f1e0", + "templates/user/code.tmpl": "sha256:d71b4aacb754938c243c71e8267874a6a4938a8f2b07e7c0fbd45743d5386263", + "templates/user/dashboard/dashboard.tmpl": "sha256:ee0f8c65214a40711aaacc5ee32c0f2551cda983828e6b911afe80f9bd14c6d4", + "templates/user/dashboard/feeds.tmpl": "sha256:39d26894272479cc6b47d6388486fce2915018df8d98a70f8f3e563dbb8fda5b", + "templates/user/dashboard/guide.tmpl": "sha256:03c4e1e42c2420ff259360e84c6ef071a80c52704e408225ea64d9f17d7eca83", + "templates/user/dashboard/issues.tmpl": "sha256:f771c3f6f9aa32660c62797815b48bf0aa54056043382cfc49a79ff0cbfabe09", + "templates/user/dashboard/milestones.tmpl": "sha256:ab049a80806215c52c151613b376229a7597bcb61719018403ceaaabdfb6c558", + "templates/user/dashboard/navbar.tmpl": "sha256:a5a99b4656bdd6aaac39d9724a732061fcc04751ff3c7735e70e08d324597ed8", + "templates/user/dashboard/repolist.tmpl": "sha256:9c06d52e3c687257e303be3e4785cef05be608b07f2bd033ae1149fb2e1f1373", + "templates/user/heatmap.tmpl": "sha256:f49905d74746239e0e91e4cd5efaa4ace1a382fdd9789fd7cc45eaf1c6bf7e93", + "templates/user/notification/notification.tmpl": "sha256:37584419b8ef78f32f010b67b538f77a956a131373b9a12a1b3742240dfca3ee", + "templates/user/notification/notification_div.tmpl": "sha256:d67787a5c25aae0ccf067997e5a08572a8a50b0bc97f8e5a1ec254419753bb40", + "templates/user/notification/notification_subscriptions.tmpl": "sha256:a206421bf01e3416151bcb0376433f422110ad95229f9a20b0da21521ec0671b", + "templates/user/overview/header.tmpl": "sha256:7f9efb433f1128c488d98eb078e07b462a316661c8fff7cb5fa74d4462d814dc", + "templates/user/overview/package_versions.tmpl": "sha256:68db31152374163d48c77edae007dcf9b7263befd8736b9f4ebfac3b718bacc1", + "templates/user/overview/packages.tmpl": "sha256:1dd3a5afa8b2a7735900e87df26b315da4f639a1102a41291b567d585a2ce33a", + "templates/user/profile.tmpl": "sha256:8ab3965620a04d393bfced2054d79a2bab479ab3f332bed06baf3bacf62d9054", + "templates/user/settings/account.tmpl": "sha256:0b8610dce2e41454d48a84ea4d6c73a9c5e73e5ae3659afbfc8339334d7d2415", + "templates/user/settings/actions.tmpl": "sha256:3892f30b05a4d0743540a73b81f8a7ac23eb46da66a1c2b5f2c928e4056fefb7", + "templates/user/settings/appearance.tmpl": "sha256:8d74bea24dbd22a644132980964c4dc9cb454b2dd8fac78120ca225820d572a5", + "templates/user/settings/applications.tmpl": "sha256:0263ca62e91289ec3da79ad368e80923e4993ad28c4a576138454a8904f03f89", + "templates/user/settings/applications_oauth2.tmpl": "sha256:82da1ade20b176d2c9c80d7b957b51ce2919a0ddb643b42cd562aef7f5aa21c9", + "templates/user/settings/applications_oauth2_edit.tmpl": "sha256:1119c308d9540bb725559d41b9e747daad6e619e67ddbb86f274571ab9be2886", + "templates/user/settings/applications_oauth2_edit_form.tmpl": "sha256:35845ec852ea0c27f29741c634d9666b8c533b75db5199f8889b8728826d8212", + "templates/user/settings/applications_oauth2_list.tmpl": "sha256:932499f8742ab32b01937e7848678e092e827c6589ec1ee620fec3b38e13c293", + "templates/user/settings/blocked_users.tmpl": "sha256:b67e666e6ebc8463ab212fe19bda6252441441b4bd3c4cf5d419b02516569efd", + "templates/user/settings/grants_oauth2.tmpl": "sha256:1d1f59a13b3676b5924869b559cc590690f306456222efb45e0f646fdc79eac6", + "templates/user/settings/hook_new.tmpl": "sha256:7625cd383ec0618e2d629900700988de74ac2e1d08fef3c529bd9110af9204fe", + "templates/user/settings/hooks.tmpl": "sha256:6edb3b075fc14376bffe507d8d61f360e0a2bb27297c5fa158651aad92d1f1ef", + "templates/user/settings/keys.tmpl": "sha256:e587ae7db2beecccd7af83b42a64b85b417c8157cd70726c38b8093e23638740", + "templates/user/settings/keys_gpg.tmpl": "sha256:4dba6f037ace6aa79fb4abc3fa32ef25b25af7d7b398a324cc30f99be1ef0bf1", + "templates/user/settings/keys_principal.tmpl": "sha256:a16c9c34f6c151c736e643cd749bfd8dfa43000039ab76d922ae8dfefa7ad81c", + "templates/user/settings/keys_ssh.tmpl": "sha256:7ded4674a5630eead2862fa3499efcf7246517c3be4f22a8eb693ef24b464c0c", + "templates/user/settings/layout_footer.tmpl": "sha256:2b09eb01efc9a035e335864c216e687c06ad3dba8f75a9199245965e6a0d04f7", + "templates/user/settings/layout_head.tmpl": "sha256:f8a32fad051ec6e3886d33db20059e9817a4eaa4a1fbbc55f1ae2f241b809a92", + "templates/user/settings/navbar.tmpl": "sha256:89e2a8870f0c758563edd79b8752b643bb184b9dadfe837d05e535679ebd1882", + "templates/user/settings/organization.tmpl": "sha256:3c96d17f793d4605c92058c1868a0c61ce546f2017e0fc421212099819801fb6", + "templates/user/settings/packages.tmpl": "sha256:b4ab20398a2218515a9024bd58c3d35d61bd6b5782d740d7edf4215d3b8bf9dc", + "templates/user/settings/packages_cleanup_rules_edit.tmpl": "sha256:9f0b67fe1bda41daa237eeecf496b9f369cb0f342c3cc72fe8530cfe55f42c29", + "templates/user/settings/packages_cleanup_rules_preview.tmpl": "sha256:429885746f0372adeca3a4e36ee17bf61e3a0354fe286714d04aa077020c3724", + "templates/user/settings/profile.tmpl": "sha256:4838565947e891856faace3b1275ddcdb39bbb66ecc022ac013c3ab62c262931", + "templates/user/settings/repos.tmpl": "sha256:686714a5b7121e7b853039621b79b1fc5f08ab84797524063ab21b6dea390de9", + "templates/user/settings/runner_edit.tmpl": "sha256:7bb4d62fb5e9ffba34c8512d057b3706bb090f5008bb255d2f36b282299434c6", + "templates/user/settings/security/accountlinks.tmpl": "sha256:ed57eb1046683b6a22ab96405b3bfccd4353eb463dca5023c8f943f960874b01", + "templates/user/settings/security/openid.tmpl": "sha256:60a139b5d1179c9debd35de45ce00e42a068bfe3d71f0a21a30d79a223b00228", + "templates/user/settings/security/security.tmpl": "sha256:756e05035eccd830927bfa14cb121b14b76d8e867f8a952037e9d2e2330cec0c", + "templates/user/settings/security/twofa.tmpl": "sha256:67ba8fc3f1da3ed7564400ae18c647857bc983c303cdd1e5c20f70993e51dfa8", + "templates/user/settings/security/twofa_enroll.tmpl": "sha256:bdacd37c1fe11d373a3d2e18afa8e87dc52ced80e26f5922b40e28da161101da", + "templates/user/settings/security/webauthn.tmpl": "sha256:ef390d4470815f43e4c198c2c263d85e3c1d3351f1f15ee2de5bd1a3b7bbb7d1", + "templates/user/settings/storage_overview.tmpl": "sha256:937c7c2516a6b888e1d1d643860f9a19c9df84276c2f87dd455dc72aea5f560c", + "templates/webhook/new.tmpl": "sha256:581de797cb9a2f83193985ec51e712f15fdd3e7c5008512ecfa114035623e73a", + "templates/webhook/new/dingtalk.tmpl": "sha256:c72f53d8ac8862be89f127f5c7dc16d857dd93b0b59da1597f354cd7c99b41b4", + "templates/webhook/new/discord.tmpl": "sha256:b5795379df73415aad01c94fb604b965649abd6f2a386576f1cad4b3d65f1405", + "templates/webhook/new/feishu.tmpl": "sha256:9e5994933cb6e282c4c688332306daaf4d2ad0b0acda7ada316b2b8105bedb17", + "templates/webhook/new/forgejo.tmpl": "sha256:796a8fd13cb9fa1ba1051f53f38b4ecaaf31fdb6b278a9d4e88842129fd261c6", + "templates/webhook/new/gitea.tmpl": "sha256:12831c611470e873efd9c9e2475053e804e91521626f7d2aeb60ea4e06c5851e", + "templates/webhook/new/gogs.tmpl": "sha256:bbdbec7a421574efdd40600210ee9f7bd4cf7964b609869b3ec7fcdf94196abe", + "templates/webhook/new/matrix.tmpl": "sha256:94740578f5b7bf20e4a9cd7a614856173876944642cb86fb92cb4a22abbb7a91", + "templates/webhook/new/msteams.tmpl": "sha256:b838064e0c7dfe885377a92092a51184deb0974577b9edbe6abd114ea15805b2", + "templates/webhook/new/packagist.tmpl": "sha256:3120e37a5a54ba8a8f9aa3db4ed9d5d4ab81b366fa623c581cdf5142d5df54bc", + "templates/webhook/new/slack.tmpl": "sha256:4a8c4a2d01c5b02fa0b1c32e712777b97775be51a33e42627a8586c1836bed0f", + "templates/webhook/new/sourcehut_builds.tmpl": "sha256:26e6f3d6a8dbf1bd382eee2d9f0b7bda87c9aac1a92284a9e995106455246775", + "templates/webhook/new/telegram.tmpl": "sha256:0909d22eddd92134fa80b2b79cfd536cec3e97b63fdee94b20ba3372b16721ea", + "templates/webhook/new/wechatwork.tmpl": "sha256:c91bb0f871a6c7f11cce22d2919d51c07b5a90b426e9ae3cc837151ae68c3b45", + "templates/webhook/shared-settings.tmpl": "sha256:94e60c17c42d10223015726bf8dda379774914d4aff125ce4db9311901a34579" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-manifest-derivation.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-manifest-derivation.json new file mode 100644 index 0000000..cae0388 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/embedded-manifest-derivation.json @@ -0,0 +1,11 @@ +{ + "scope": "Public generated/embedded asset SHA-256 identifiers only", + "assets_verified_against_both_actual_source_and_extracted_binary": 2234, + "runtime_original_manifest_sha256": "sha256:74a0ec278ab99ac86ee434d7620229dc4a70146efd51876a1e4738d2c9f4ffdb", + "published_manifest_sha256": "sha256:ca09d0b2ed436ad6d3473da35d0d1081bdd8c3311825f4684993b33e6b426736", + "original_byte_exact_reconstructed": true, + "reconstruction": "Remove the sha256: value prefix and serialize with json.dumps(indent=2, sort_keys=True) plus one newline.", + "runtime_verification_receipt_preserved": true, + "scanner_rules_or_allowlists_changed": false, + "credential_values_transformed": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/frontend-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/frontend-result.json new file mode 100644 index 0000000..25d0251 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/frontend-result.json @@ -0,0 +1,13 @@ +{ + "argv": [ + "make", + "GOFLAGS=-trimpath -buildvcs=false -p=4", + "RELEASE_VERSION=11.0.3+cust80.feb10793", + "FORGEJO_VERSION=11.0.3+cust80.feb10793", + "FORGEJO_VERSION_API=11.0.3+cust80.feb10793", + "frontend" + ], + "phase": "frontend", + "returncode": 0, + "seconds": 83.5 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/image-build-metadata.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/image-build-metadata.json new file mode 100644 index 0000000..bb50ae0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/image-build-metadata.json @@ -0,0 +1,55 @@ +{ + "buildx.build.provenance": { + "builder": { + "id": "" + }, + "buildType": "https://mobyproject.org/buildkit@v1", + "materials": [ + { + "uri": "pkg:docker/code.forgejo.org/forgejo/forgejo?digest=sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b\u0026platform=linux%2Famd64", + "digest": { + "sha256": "41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b" + } + } + ], + "invocation": { + "configSource": { + "entryPoint": "Dockerfile" + }, + "parameters": { + "frontend": "dockerfile.v0", + "args": { + "build-arg:SOURCE_DATE_EPOCH": "1752120742", + "force-network-mode": "none", + "no-cache": "" + }, + "locals": [ + { + "name": "context" + }, + { + "name": "dockerfile" + } + ] + }, + "environment": { + "platform": "linux/amd64" + } + } + }, + "buildx.build.ref": "default/default/slt9p94vejq7ji1ub5j63htim", + "containerimage.config.digest": "sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b", + "containerimage.descriptor": { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "size": 2841, + "annotations": { + "org.opencontainers.image.created": "2025-07-10T04:12:22Z" + }, + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + "containerimage.digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7" +} \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/new-cli-help-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/new-cli-help-result.json new file mode 100644 index 0000000..f2fe594 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/new-cli-help-result.json @@ -0,0 +1,60 @@ +{ + "argv": [ + "bwrap", + "--unshare-net", + "--unshare-pid", + "--proc", + "/proc", + "--dev", + "/dev", + "--tmpfs", + "/tmp", + "--dir", + "/app", + "--ro-bind", + "/tmp/custodian-forgejo-release-r4/source/gitea", + "/app/forgejo", + "--bind", + "/tmp/custodian-forgejo-release-r4/embedded-extracted", + "/results", + "--chdir", + "/tmp", + "--clearenv", + "--setenv", + "PATH", + "/nonexistent", + "--setenv", + "HOME", + "/tmp/home", + "--setenv", + "USER", + "builder", + "--setenv", + "USERNAME", + "builder", + "--setenv", + "GITEA_WORK_DIR", + "/tmp/work", + "--setenv", + "GITEA_CUSTOM", + "/tmp/custom", + "--setenv", + "GITEA_APP_INI", + "/tmp/absent-app.ini", + "--setenv", + "TZ", + "UTC", + "--setenv", + "LC_ALL", + "C", + "--", + "/app/forgejo", + "admin", + "auth", + "add-oauth", + "--help" + ], + "phase": "new-cli-help", + "returncode": 0, + "seconds": 0.13 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/npm-offline-ci-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/npm-offline-ci-result.json new file mode 100644 index 0000000..74325fd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/npm-offline-ci-result.json @@ -0,0 +1,13 @@ +{ + "argv": [ + "npm", + "ci", + "--offline", + "--ignore-scripts", + "--no-audit", + "--no-fund" + ], + "phase": "npm-offline-ci", + "returncode": 0, + "seconds": 32.83 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/prepared.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/prepared.json new file mode 100644 index 0000000..0ff0b79 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/prepared.json @@ -0,0 +1,5 @@ +{ + "lock_sha256": "5a4d0e5126b6b9dd66fe2828ff78bfe6ac0cadc34b96ca739225d622b963b768", + "module_cache": "/tmp/custodian-forgejo-successor-attempt2/modules", + "public_archives_verified": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build-result.json new file mode 100644 index 0000000..df0d31d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build-result.json @@ -0,0 +1,24 @@ +{ + "argv": [ + "docker", + "buildx", + "build", + "--output", + "type=oci,dest=/tmp/custodian-forgejo-release-r4-oci-replay/image.oci.tar,rewrite-timestamp=true", + "--no-cache", + "--platform", + "linux/amd64", + "--pull=false", + "--network=none", + "--provenance=false", + "--sbom=false", + "--metadata-file", + "/tmp/custodian-forgejo-release-r4-oci-replay/image-build-metadata.json", + "--build-arg", + "SOURCE_DATE_EPOCH=1752120742", + "/tmp/custodian-forgejo-release-r4-oci-replay/container-context" + ], + "phase": "container-build", + "returncode": 0, + "seconds": 24.94 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build.log b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build.log new file mode 100644 index 0000000..50509af --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-build.log @@ -0,0 +1,38 @@ +#0 building with "default" instance using docker driver + +#1 [internal] load build definition from Dockerfile +#1 transferring dockerfile: 465B done +#1 DONE 0.0s + +#2 [internal] load metadata for code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b +#2 DONE 0.0s + +#3 [internal] load .dockerignore +#3 transferring context: +#3 transferring context: 2B done +#3 DONE 0.0s + +#4 [1/2] FROM code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b +#4 resolve code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b 0.0s done +#4 CACHED + +#5 [internal] load build context +#5 transferring context: 111.05MB 1.6s done +#5 DONE 1.6s + +#6 [2/2] COPY --chown=0:0 --chmod=755 gitea /app/gitea/gitea +#6 DONE 0.6s + +#7 exporting to oci image format +#7 exporting layers +#7 exporting layers 8.3s done +#7 rewriting layers with source-date-epoch 1752120742 (2025-07-10 04:12:22 +0000 UTC) +#7 rewriting layers with source-date-epoch 1752120742 (2025-07-10 04:12:22 +0000 UTC) 7.5s done +#7 exporting manifest sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7 done +#7 exporting config sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b done +#7 sending tarball +#7 sending tarball 6.2s done +#7 DONE 22.1s + +#8 resolving provenance for metadata file +#8 DONE 0.0s diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-result.json new file mode 100644 index 0000000..333a66a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-container-result.json @@ -0,0 +1,17 @@ +{ + "all_base_layers_and_rootless_runtime_config_preserved": true, + "all_oci_blob_digests_verified": true, + "archive_sha256": "499ef48172540b07f01de62e0c78f5798fe2793cac73d0c6188b37c07077e7d0", + "artifact_path": "/tmp/custodian-forgejo-release-r4-oci-replay/image.oci.tar", + "config_digest": "sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b", + "container_epoch": 1752120742, + "deployment_tag": null, + "final_layer_binary_exact": true, + "host_or_secret_mounts": false, + "image_manifest_digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "local_runtime_image_loaded": false, + "oci_index_sha256": "0f283c23a4c7fb461a4f7a745e8bfd95f8def8e67772bbbae43b781e61e92ed7", + "privileged_containers": false, + "registry_publication": false, + "source_epoch": 1752105237 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-image-build-metadata.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-image-build-metadata.json new file mode 100644 index 0000000..66d5035 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/replay-image-build-metadata.json @@ -0,0 +1,55 @@ +{ + "buildx.build.provenance": { + "builder": { + "id": "" + }, + "buildType": "https://mobyproject.org/buildkit@v1", + "materials": [ + { + "uri": "pkg:docker/code.forgejo.org/forgejo/forgejo?digest=sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b\u0026platform=linux%2Famd64", + "digest": { + "sha256": "41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b" + } + } + ], + "invocation": { + "configSource": { + "entryPoint": "Dockerfile" + }, + "parameters": { + "frontend": "dockerfile.v0", + "args": { + "build-arg:SOURCE_DATE_EPOCH": "1752120742", + "force-network-mode": "none", + "no-cache": "" + }, + "locals": [ + { + "name": "context" + }, + { + "name": "dockerfile" + } + ] + }, + "environment": { + "platform": "linux/amd64" + } + } + }, + "buildx.build.ref": "default/default/rrs7gz2v5zjreemkztsc0qxx8", + "containerimage.config.digest": "sha256:864ee9249f5e00b5e047f125a640f666ba132f92bd5fbed1084a51b6a644b76b", + "containerimage.descriptor": { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "size": 2841, + "annotations": { + "org.opencontainers.image.created": "2025-07-10T04:12:22Z" + }, + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + "containerimage.digest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7" +} \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verification.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verification.json new file mode 100644 index 0000000..2f768d3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verification.json @@ -0,0 +1,13 @@ +{ + "all_public_options_templates_exact": true, + "architecture_approval": false, + "binary_sha256": "92efb26614d3fca22c400920d92c4cd022e32ab86c1f562f60699ab5f750cd28", + "embedded_file_manifest_sha256": "74a0ec278ab99ac86ee434d7620229dc4a70146efd51876a1e4738d2c9f4ffdb", + "embedded_files": 2234, + "native_admission": false, + "network": "bwrap --unshare-net", + "new_cli_flag_present": true, + "no_dynamic_interpreter": true, + "no_source_assets_libraries_or_host_config_mounted": true, + "source_locks_preserved": true +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verify-go-modules-result.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verify-go-modules-result.json new file mode 100644 index 0000000..831eff7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-evidence/verify-go-modules-result.json @@ -0,0 +1,10 @@ +{ + "argv": [ + "go", + "mod", + "verify" + ], + "phase": "verify-go-modules", + "returncode": 0, + "seconds": 6.15 +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/README.md b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/README.md new file mode 100644 index 0000000..9a0562e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/README.md @@ -0,0 +1,136 @@ +# Forgejo release preparation (build only) + +The Apps Makefile targets package the digest-bound 40-path Forgejo 11.0.3 +source candidate in `forgejo-security-extension.patch`. It combines the original +three-group any-of gate, private secret-file input, private source-operation DB +primitives and the producer-owned Access Engine client in the OIDC callback. +Coordinator source tests and complete release checks are recorded separately; +independent owner review and operational admission of this revision remain open. They never deploy, push, change default values or choose +an operational image. Third-party application source is expanded only into a new +`/tmp` workspace; this repository retains a release input lock and exact reviewed +patch, not a second source-code tree. Upstream source ownership and publication +remain separate owner work. + +Supply explicit public input archives/cache paths to `make forgejo-release-prepare`: +`FORGEJO_SOURCE_ARCHIVE`, `FORGEJO_GO_ARCHIVE`, `FORGEJO_NODE_ARCHIVE`, +`FORGEJO_BUILD_GOMODCACHE`, optionally `FORGEJO_BUILD_GOCACHE_SEED` and +`FORGEJO_BUILD_NPM_CACHE_SEED`. Every archive, patch, source output, dependency +lock and selected host compiler tool is digest-bound by `inputs.lock.json`. +Go's official checksums and Node's official SHASUMS must be independently checked +when acquiring those public archives. The source archive is official Forgejo +v11.0.3; archive PAX commit b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237. + +The workspace must not exist. Existing trials are preserved, never restarted +because an observer timed out. The recipe's `process.json` records the exact +active child PID, phase and command, then completion; logs and receipts stay in +the workspace. Do not run two phases against the same workspace concurrently. + +`make forgejo-release-acquire-npm` explicitly acquires only public registry +packages, with fresh HOME/config paths, integrity-bound lockfile and lifecycle +scripts disabled. With the cache ready, `make forgejo-release-build` reruns +`npm ci --offline --ignore-scripts`, Go module verification, upstream frontend +compilation and upstream generation/static backend targets inside a network +namespace without connectivity. It uses the exact Go/Node distributions, local +compiler inputs, `bindata timetzdata sqlite sqlite_unlock_notify` plus upstream +static target's `netgo osusergo`, explicit Make GOFLAGS for trimpath/buildvcs and +fixed file/directory timestamps. Build tags retain the upstream rootless build +profile. `make forgejo-release-verify` compares every embedded public/options/ +template file to actual generated source bytes, checks the extension CLI and +checks the ELF has no dynamic interpreter. These are local artifact checks, +not application login, DB, native rollout or architecture acceptance. + +For a **local container build** only, first acquire the locked public amd64 +base with an empty Docker client config and the explicit local socket. Then +`make forgejo-release-container` uses that exact digest as a build input and a +minimal context containing only the verified executable and generated Dockerfile. +It directly exports a local OCI archive with `rewrite-timestamp=true`, no-cache, +no image load or push. SOURCE_DATE_EPOCH alone does not fix COPY parent-directory +timestamps. The image epoch is max(source epoch,ceil immutable base created +instant), so the original base layers remain exact while new metadata is stable. +The recipe verifies every OCI blob, original base layer prefix, rootless runtime +configuration, provenance labels and exact final executable. Two no-cache exports +must reproduce the frozen output image and archive digests before claiming +reproducibility. The actual local proof receipts are in the accompanying source +proposal packet, not an admission of the resulting artifact. + +There is no deployment tag, registry publication, RUN directive, privileged +container or Docker host/secret mount. Public base acquisition and a standard +local Docker/buildx daemon supporting OCI export/layer timestamp rewriting are +explicit prerequisites. An unavailable feature is a retained blocker; do not +switch to a privileged builder or claim a successful image from a backend-only +binary. Existing contexts/output archives refuse overwrite. Reconcile an actual +failed export explicitly, retaining original metadata/logs/artifacts; observation +timeouts never justify another build. Older prototype load/save behavior and +its config-versus-manifest-ID issue are retained in the source trial history, +not shipped as a production continuation. + +The static host toolchain is pinned for this Linux amd64 review estate. Broader +cross-host reproducibility or a different architecture/compiler/libc is a new +input revision, not an implicit fallback. The glibc static-linker's optional +SQLite `dlopen` diagnostic is retained; no SQLite-extension runtime compatibility +or production platform admission is inferred from ELF inspection or asset checks. +An accepted owner image/source/backend, authenticated access-engine decision, +private secret-input boundary, type/case/descriptor re-freeze, current operation/ +inverse sessions, durable journal and application/Helm writer exclusion are +separate gates. Helm omission/rollback does not remove persisted OAuth source. +The existing case gate is compatibility code, not a single-PDP exception. + +## KeyCape identity and callback admission + +The callback independently verifies the ID token against owner-admitted public +keys, algorithms, issuer, audience and token type. Public trust is loaded from +`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE`; only an absolute private file reference +is used for the PDP caller token. No admission file or operational keys are +supplied by this build. New fields `tenant_source_claim` and +`max_authentication_age` are mandatory. The KeyCape v0.3 profile uses +`assurance_claim: assurance` and `tenant_source_claim: tenant_source`. Maximum +identity age and maximum authentication age are separate positive durations +chosen by the owner, not implicit defaults. + +The signed assurance object preserves `level`, `methods`, boolean `mfa`, +`source: key-cape` and integer `at`. Its authentication time must not follow +issuance or the current time, and must satisfy the admitted authentication-age +bound. Identity validity ends at the earliest token expiration, issued-at age +bound or authentication-age bound. Directory, registration and default tenant +provenance remain distinct in the request. Mutable UserInfo cannot replace +these authenticated facts. Current UserInfo subject and the Goth subject must +match the independently verified token; the three required group alternatives +remain policy input. + +The parser also represents valid lower-assurance facts. Application policy +continues to decide authorization. The Check client and unchanged canonical +signature bytes are a generated source slice of Access Engine ca0cb757; Forgejo +adds no policy evaluator. Exact caller/policy/registry/signer/request/lifetime +pins, an authenticated HTTPS connection and a current ALLOW are required. +Synthetic callback fixtures test ALLOW, DENY and adverse cases, including an ID +token serialized by the actual pinned KeyCape handler. Those fixtures do not +prove an actual browser, factor, custody or native rollout. + +The current integration covers this guarded OIDC callback. Ongoing application +HTTP/API/Git/SSH paths, current operation/inverse sessions, durable private +evidence and retention, deployed PostgreSQL/custody acceptance and exclusion of all release +writers remain open in RAPPS-WP-0016-T08/T09 and NK-WP-0048-T43. An old image +approval or source test result cannot admit this candidate. + +## PostgreSQL operation-source verification + +The source primitive has now run on a disposable PostgreSQL 16.14 instance using +the actual Forgejo engine, model schemas and OAuth configuration types. Tests +cover default, explicit-public and named schemas, exact source restoration, drift +and linked-account refusals, trigger rollback, uncooperative SQL writer exclusion +and concurrent preparation. Independent client processes and an actual server +restart verify prepared/applied/inverse journal recovery. The test-only +`source_operation_postgres` tag selects the integration tests; no runtime route, +credential, session or authority callback is added. The companion Custodian +`forgejo-postgres-source-r1` packet holds the isolated fixture, commands, source +pins, counterexamples and original failed attempts. + +Actual PostgreSQL diagnostics exposed a tracing-hook composition crash. Tracing +now publishes its task context to the shared XORM hook context so later hooks +retain it. Ordinary and private SQLite traces and actual PostgreSQL failure +queries exercise this path. Controlled copies without the table lock or private +query marker fail their respective regressions. These are source/database +mechanics checks; owner admission, current authenticated operation/inverse +sessions, runtime/backup custody, retention, every release writer and native +deployed-database acceptance remain separate obligations. The previous frozen +release is historical and does not contain this context correction. diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/forgejo-security-extension.patch b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/forgejo-security-extension.patch new file mode 100644 index 0000000..9465e3f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/forgejo-security-extension.patch @@ -0,0 +1,5001 @@ +--- a/cmd/admin_auth_oauth.go ++++ b/cmd/admin_auth_oauth.go +@@ -9,6 +9,7 @@ + "net/url" + + auth_model "forgejo.org/models/auth" ++ "forgejo.org/models/db" + "forgejo.org/services/auth/source/oauth2" + + "github.com/urfave/cli/v2" +@@ -35,6 +36,10 @@ + Name: "secret", + Value: "", + Usage: "Client Secret", ++ }, ++ &cli.StringFlag{ ++ Name: "secret-file", ++ Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file", + }, + &cli.StringFlag{ + Name: "auto-discover-url", +@@ -94,6 +99,10 @@ + Name: "required-claim-value", + Value: "", + Usage: "Claim value that has to be set to allow users to login with this source", ++ }, ++ &cli.StringSliceFlag{ ++ Name: "required-claim-values", ++ Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)", + }, + &cli.StringFlag{ + Name: "group-claim-name", +@@ -160,6 +169,7 @@ + Scopes: c.StringSlice("scopes"), + RequiredClaimName: c.String("required-claim-name"), + RequiredClaimValue: c.String("required-claim-value"), ++ RequiredClaimValues: c.StringSlice("required-claim-values"), + GroupClaimName: c.String("group-claim-name"), + AdminGroup: c.String("admin-group"), + RestrictedGroup: c.String("restricted-group"), +@@ -169,14 +179,31 @@ + } + + func runAddOauth(c *cli.Context) error { ++ secret, selected, err := oauthSecretInput(c) ++ if err != nil { ++ return err ++ } + ctx, cancel := installSignals() + defer cancel() ++ ctx = db.WithSensitiveSQL(ctx) + + if err := initDB(ctx); err != nil { + return err + } ++ if db.SQLLoggingEnabled(ctx) { ++ return errors.New("OAuth configuration requires SQL logging disabled") ++ } + + config := parseOAuth2Config(c) ++ if selected { ++ config.ClientSecret = secret ++ } ++ if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil { ++ return err ++ } ++ if config.RequiredClaimValues != nil && config.Provider != "openidConnect" { ++ return errors.New("required-claim-values requires openidConnect") ++ } + if config.Provider == "openidConnect" { + discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL) + if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { +@@ -184,24 +211,36 @@ + } + } + +- return auth_model.CreateSource(ctx, &auth_model.Source{ ++ err = auth_model.CreateSource(ctx, &auth_model.Source{ + Type: auth_model.OAuth2, + Name: c.String("name"), + IsActive: true, + Cfg: config, + }) ++ if err != nil && c.IsSet("secret-file") { ++ return errors.New("OAuth source creation failed") ++ } ++ return err + } + + func runUpdateOauth(c *cli.Context) error { ++ secret, selected, err := oauthSecretInput(c) ++ if err != nil { ++ return err ++ } + if !c.IsSet("id") { + return errors.New("--id flag is missing") + } + + ctx, cancel := installSignals() + defer cancel() ++ ctx = db.WithSensitiveSQL(ctx) + + if err := initDB(ctx); err != nil { + return err ++ } ++ if db.SQLLoggingEnabled(ctx) { ++ return errors.New("OAuth configuration requires SQL logging disabled") + } + + source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) +@@ -209,7 +248,10 @@ + return err + } + +- oAuth2Config := source.Cfg.(*oauth2.Source) ++ oAuth2Config, ok := source.Cfg.(*oauth2.Source) ++ if !ok { ++ return errors.New("authentication source is not OAuth2") ++ } + + if c.IsSet("name") { + source.Name = c.String("name") +@@ -223,8 +265,8 @@ + oAuth2Config.ClientID = c.String("key") + } + +- if c.IsSet("secret") { +- oAuth2Config.ClientSecret = c.String("secret") ++ if selected { ++ oAuth2Config.ClientSecret = secret + } + + if c.IsSet("auto-discover-url") { +@@ -244,6 +286,10 @@ + } + if c.IsSet("required-claim-value") { + oAuth2Config.RequiredClaimValue = c.String("required-claim-value") ++ } ++ ++ if c.IsSet("required-claim-values") { ++ oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values") + } + + if c.IsSet("group-claim-name") { +@@ -293,7 +339,30 @@ + } + + oAuth2Config.CustomURLMapping = customURLMapping ++ if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil { ++ return err ++ } ++ if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" { ++ return errors.New("required-claim-values requires openidConnect") ++ } + source.Cfg = oAuth2Config + +- return auth_model.UpdateSource(ctx, source) ++ err = auth_model.UpdateSource(ctx, source) ++ if err != nil && c.IsSet("secret-file") { ++ return errors.New("OAuth source update failed") ++ } ++ return err + } ++ ++// Resolve before opening the DB or mutating a loaded source. Never store the ++// resolved bytes in the CLI context: they must not become flag/argv diagnostics. ++func oauthSecretInput(c *cli.Context) (string, bool, error) { ++ if c.IsSet("secret-file") { ++ if c.IsSet("secret") { ++ return "", false, errors.New("secret and secret-file are mutually exclusive") ++ } ++ value, err := readOAuthSecretFile(c.String("secret-file")) ++ return value, true, err ++ } ++ return c.String("secret"), c.IsSet("secret"), nil ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_linux.go +@@ -0,0 +1,114 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build linux ++ ++package cmd ++ ++import ( ++ "errors" ++ "io" ++ "os" ++ "path/filepath" ++ "strings" ++ ++ "golang.org/x/sys/unix" ++) ++ ++var errOAuthSecretFile = errors.New("secret-file input refused") ++ ++// This checks DAC and mount properties, not namespace, Secret provenance or ++// operation authority. Admission must bind the exact workload/mount/reader set. ++// Kubernetes Secret subPath leaves are root-owned; fsGroup can provide 0440 ++// access. Requiring euid ownership would incorrectly refuse that supported case. ++func readOAuthSecretFile(path string) (string, error) { ++ if len(path) > 4096 || !filepath.IsAbs(path) || filepath.Clean(path) != path || strings.ContainsRune(path, 0) { ++ return "", errOAuthSecretFile ++ } ++ parts := strings.Split(strings.TrimPrefix(path, "/"), "/") ++ if len(parts) < 2 { ++ return "", errOAuthSecretFile ++ } ++ dir, err := unix.Open("/", unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ defer func() { unix.Close(dir) }() ++ for _, part := range parts[:len(parts)-1] { ++ var st unix.Stat_t ++ if unix.Fstat(dir, &st) != nil || !safeOAuthSecretParent(st) { ++ return "", errOAuthSecretFile ++ } ++ next, err := unix.Openat(dir, part, unix.O_PATH|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ unix.Close(dir) ++ dir = next ++ } ++ var parent unix.Stat_t ++ if unix.Fstat(dir, &parent) != nil || !safeOAuthSecretParent(parent) { ++ return "", errOAuthSecretFile ++ } ++ // Nonblocking is necessary: reject a FIFO without waiting for a writer. ++ fd, err := unix.Openat(dir, parts[len(parts)-1], unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_NONBLOCK|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return "", errOAuthSecretFile ++ } ++ f := os.NewFile(uintptr(fd), "private OAuth input") ++ defer f.Close() ++ return readOAuthSecretFD(f, dir, parts[len(parts)-1]) ++} ++ ++func readOAuthSecretFD(f *os.File, dir int, leaf string) (string, error) { ++ fd := int(f.Fd()) ++ var before, after, named unix.Stat_t ++ var mount unix.Statfs_t ++ if unix.Fstat(fd, &before) != nil || !safeOAuthSecretLeaf(before) || unix.Fstatfs(fd, &mount) != nil || mount.Flags&unix.ST_RDONLY == 0 { ++ return "", errOAuthSecretFile ++ } ++ // mint-secret uses secrets.token_urlsafe(48): exactly 64 ASCII characters, ++ // no padding/newline. Bounded read still detects a concurrent size increase. ++ data, err := io.ReadAll(io.LimitReader(f, 65)) ++ defer clear(data) ++ if err != nil || len(data) != 64 { ++ return "", errOAuthSecretFile ++ } ++ for _, b := range data { ++ if !(b >= 'A' && b <= 'Z' || b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '_') { ++ return "", errOAuthSecretFile ++ } ++ } ++ if unix.Fstat(fd, &after) != nil || !sameOAuthSecretFile(before, after) || unix.Fstatat(dir, leaf, &named, unix.AT_SYMLINK_NOFOLLOW) != nil || !sameOAuthSecretFile(before, named) { ++ return "", errOAuthSecretFile ++ } ++ return string(data), nil ++} ++ ++func safeOAuthSecretParent(st unix.Stat_t) bool { ++ return st.Mode&unix.S_IFMT == unix.S_IFDIR && (st.Uid == 0 || st.Uid == uint32(os.Geteuid())) && st.Mode&0022 == 0 ++} ++ ++func safeOAuthSecretLeaf(st unix.Stat_t) bool { ++ if st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 || st.Size != 64 || st.Mode&07337 != 0 || (st.Uid != 0 && st.Uid != uint32(os.Geteuid())) { ++ return false ++ } ++ if st.Mode&0040 != 0 { ++ groups, err := os.Getgroups() ++ if err != nil { ++ return false ++ } ++ member := st.Gid == uint32(os.Getegid()) ++ for _, gid := range groups { ++ member = member || st.Gid == uint32(gid) ++ } ++ if !member { ++ return false ++ } ++ } ++ return true ++} ++ ++func sameOAuthSecretFile(a, b unix.Stat_t) bool { ++ return a.Dev == b.Dev && a.Ino == b.Ino && a.Mode == b.Mode && a.Nlink == b.Nlink && a.Uid == b.Uid && a.Gid == b.Gid && a.Size == b.Size && a.Mtim == b.Mtim && a.Ctim == b.Ctim ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_linux_test.go +@@ -0,0 +1,79 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build linux ++ ++package cmd ++ ++import ( ++ "golang.org/x/sys/unix" ++ "os" ++ "testing" ++) ++ ++func TestPrivateSecretMetadata(t *testing.T) { ++ st := unix.Stat_t{Mode: unix.S_IFREG | 0440, Uid: 0, Gid: uint32(os.Getegid()), Nlink: 1, Size: 64} ++ if !safeOAuthSecretLeaf(st) { ++ t.Fatal("root-owned fsGroup-readable Secret subPath refused") ++ } ++ for _, mode := range []uint32{0444, 0640, 0540, 0460, 0441, 0440 | unix.S_ISUID, 0440 | unix.S_ISGID, 0440 | unix.S_ISVTX} { ++ x := st ++ x.Mode = unix.S_IFREG | mode ++ if safeOAuthSecretLeaf(x) { ++ t.Fatalf("unsafe mode accepted: %o", mode) ++ } ++ } ++ for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Uid = 123456 }, func(x *unix.Stat_t) { x.Nlink = 2 }, func(x *unix.Stat_t) { x.Size = 63 }, func(x *unix.Stat_t) { x.Mode = unix.S_IFIFO | 0400 }, func(x *unix.Stat_t) { x.Gid = 123456 }} { ++ x := st ++ mutate(&x) ++ if safeOAuthSecretLeaf(x) { ++ t.Fatal("unsafe metadata accepted") ++ } ++ } ++ if !safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0755, Uid: 0}) { ++ t.Fatal("root parent refused") ++ } ++ if safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0775, Uid: 0}) { ++ t.Fatal("group writable parent accepted") ++ } ++ for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Dev++ }, func(x *unix.Stat_t) { x.Ino++ }, func(x *unix.Stat_t) { x.Ctim.Nsec++ }, func(x *unix.Stat_t) { x.Mtim.Nsec++ }, func(x *unix.Stat_t) { x.Size++ }, func(x *unix.Stat_t) { x.Uid++ }, func(x *unix.Stat_t) { x.Gid++ }, func(x *unix.Stat_t) { x.Nlink++ }, func(x *unix.Stat_t) { x.Mode++ }} { ++ x := st ++ mutate(&x) ++ if sameOAuthSecretFile(st, x) { ++ t.Fatal("changed file considered stable") ++ } ++ } ++ x := st ++ x.Atim.Nsec++ ++ if !sameOAuthSecretFile(st, x) { ++ t.Fatal("read-induced atime change refused") ++ } ++} ++ ++// Actual Linux descriptors and namespace read-only bind mounts, not mocked stat ++// metadata. A writable alias lets the fixture simulate replacement after open. ++func TestPrivateSecretOpenedFileReplacement(t *testing.T) { ++ if _, err := os.Stat("/run/forgejo-oidc/race"); os.IsNotExist(err) { ++ t.Skip("requires isolated read-only bind fixture") ++ } ++ dir, err := unix.Open("/run/forgejo-oidc", unix.O_PATH|unix.O_DIRECTORY, 0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer unix.Close(dir) ++ f, err := os.Open("/run/forgejo-oidc/race") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer f.Close() ++ // Preserve the opened inode with one link; replace only the named leaf. ++ if err := os.Rename("/run/writable-input/race", "/run/writable-input/race-backup"); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.Rename("/run/writable-input/race-replacement", "/run/writable-input/race"); err != nil { ++ t.Fatal(err) ++ } ++ if value, err := readOAuthSecretFD(f, dir, "race"); err != errOAuthSecretFile || value != "" { ++ t.Fatal("replaced leaf accepted") ++ } ++} +--- /dev/null ++++ b/cmd/admin_auth_oauth_secret_other.go +@@ -0,0 +1,12 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++//go:build !linux ++ ++package cmd ++ ++import "errors" ++ ++func readOAuthSecretFile(string) (string, error) { ++ return "", errors.New("secret-file input refused") ++} +--- a/go.mod ++++ b/go.mod +@@ -255,3 +255,7 @@ + replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 + + replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 ++ ++require github.com/netkingdom/access-engine v0.0.0 ++ ++replace github.com/netkingdom/access-engine => ./third_party/access-engine-pep +--- /dev/null ++++ b/models/auth/source_operation.go +@@ -0,0 +1,430 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package auth ++ ++// This owner-confined database primitive is NOT an authorization boundary. ++// No CLI/web/API route exposes it. The admitted runtime must separately obtain ++// fresh authenticated apply/inverse sessions and retain signed public evidence. ++// Configurations and private journal rows must never leave database custody. ++ ++import ( ++ "context" ++ "encoding/json" ++ "errors" ++ "reflect" ++ "regexp" ++ "sort" ++ ++ "forgejo.org/models/db" ++ "forgejo.org/modules/setting" ++ "forgejo.org/modules/timeutil" ++ "xorm.io/xorm" ++) ++ ++var ( ++ ErrSourceOperationConflict = errors.New("source operation requires owner reconciliation") ++ ErrSourceOperationStorage = errors.New("source operation storage failure; reconcile before retry") ++ ErrSourceOperationUnsupported = errors.New("source operation requires standalone SQLite/PostgreSQL transaction with SQL logging disabled") ++ operationIDPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._:-]{7,127}$`) ++) ++ ++// Private source rows deliberately bypass Config decoding: decoding and ++// reserializing can erase unknown fields, including a gate on an older image. ++type sourceOperationRow struct { ++ ID int64 `xorm:"pk autoincr"` ++ Type Type ++ Name string ++ IsActive bool ++ IsSyncEnabled bool ++ Cfg string `xorm:"TEXT"` ++ CreatedUnix timeutil.TimeStamp ++ UpdatedUnix timeutil.TimeStamp ++} ++ ++func (sourceOperationRow) TableName() string { return "login_source" } ++ ++// The whole inventory, including private configs, stays in the owner's database. ++// Neither journal payload nor its digest is a public result. ++type sourceOperationJournal struct { ++ OperationID string `xorm:"pk VARCHAR(128)"` ++ CaseID string `xorm:"VARCHAR(128)"` ++ TargetName string ++ TargetID int64 ++ Phase string ++ BeforeState string `xorm:"TEXT"` ++ DesiredState string `xorm:"TEXT"` ++ PostState string `xorm:"TEXT"` ++} ++ ++func (sourceOperationJournal) TableName() string { return "login_source_operation" } ++func init() { db.RegisterModel(new(sourceOperationJournal)) } ++ ++// SourceOperationResult contains only public outcome metadata. An authenticated ++// session/evidence producer must bind this result to its actual principal, ++// admitted source revision, exact parameters and current operation session. ++type SourceOperationResult struct { ++ OperationID string ++ Phase string ++ SourceID int64 ++} ++ ++// sourceOperationTx acquires a database-enforced lock before any source read. ++// SQLite's first write serializes every database writer, including stock admin ++// CRUD. PostgreSQL table locks exclude ordinary SQL source and user-link writes. ++// Privileged DDL, process/provider refresh and release writers are outside this ++// bounded primitive; they still require owner runtime/release exclusion. ++func sourceOperationTx(ctx context.Context, f func(context.Context) error) error { ++ if db.InTransaction(ctx) || setting.Database.LogSQL || (!setting.Database.Type.IsSQLite3() && !setting.Database.Type.IsPostgreSQL()) { ++ return ErrSourceOperationUnsupported ++ } ++ err := db.WithTx(db.WithSensitiveSQL(ctx), func(tx context.Context) error { ++ session, ok := db.GetEngine(tx).(*xorm.Session) ++ if !ok || session.Engine().Logger().IsShowSQL() { ++ return ErrSourceOperationUnsupported ++ } ++ if setting.Database.Type.IsPostgreSQL() { ++ if _, err := db.GetEngine(tx).Exec(`LOCK TABLE login_source_operation, login_source, "user", external_login_user IN EXCLUSIVE MODE`); err != nil { ++ return err ++ } ++ } else { ++ // Even with no matching row, UPDATE starts SQLite's write transaction. ++ if _, err := db.GetEngine(tx).Exec(`UPDATE login_source_operation SET phase = phase WHERE operation_id = ''`); err != nil { ++ return err ++ } ++ } ++ return f(tx) ++ }) ++ if err == nil || errors.Is(err, ErrSourceOperationConflict) || errors.Is(err, ErrSourceOperationUnsupported) || IsErrSourceInUse(err) { ++ return err ++ } ++ return ErrSourceOperationStorage ++} ++ ++func sourceInventory(ctx context.Context) ([]sourceOperationRow, error) { ++ rows := make([]sourceOperationRow, 0) ++ err := db.GetEngine(ctx).OrderBy("id").Find(&rows) ++ return rows, err ++} ++func encodeSourceInventory(rows []sourceOperationRow) string { ++ b, _ := json.Marshal(rows) ++ return string(b) ++} ++func decodeSourceInventory(s string) ([]sourceOperationRow, error) { ++ var rows []sourceOperationRow ++ if err := json.Unmarshal([]byte(s), &rows); err != nil { ++ return nil, ErrSourceOperationConflict ++ } ++ return rows, nil ++} ++func sourceJournal(ctx context.Context, operationID string) (*sourceOperationJournal, error) { ++ row := new(sourceOperationJournal) ++ found, err := db.GetEngine(ctx).ID(operationID).Get(row) ++ if err != nil { ++ return nil, err ++ } ++ if !found { ++ return nil, ErrSourceOperationConflict ++ } ++ return row, nil ++} ++func journalResult(j *sourceOperationJournal) SourceOperationResult { ++ return SourceOperationResult{OperationID: j.OperationID, Phase: j.Phase, SourceID: j.TargetID} ++} ++ ++// PrepareSourceOperation durably captures exact source identity and complete ++// original inventory before mutation. desired is private owner input, not an ++// authorization token. targetID=0 requires authoritative name absence. Existing ++// sources require both exact ID and name. Reuse of an operation ID is refused. ++func PrepareSourceOperation(ctx context.Context, operationID, caseID string, targetID int64, desired *Source) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ if !operationIDPattern.MatchString(operationID) || !operationIDPattern.MatchString(caseID) || desired == nil || desired.Type != OAuth2 || desired.Name == "" || desired.Cfg == nil || desired.IsSyncEnabled || desired.ID != targetID { ++ return result, ErrSourceOperationConflict ++ } ++ cfg, err := desired.Cfg.ToDB() ++ if err != nil || len(cfg) > 64*1024 || !json.Valid(cfg) { ++ return result, ErrSourceOperationConflict ++ } ++ candidate := sourceOperationRow{ID: targetID, Type: OAuth2, Name: desired.Name, IsActive: desired.IsActive, Cfg: string(cfg)} ++ candidateBytes, _ := json.Marshal(candidate) ++ err = sourceOperationTx(ctx, func(tx context.Context) error { ++ // Bound confidential duplicate custody. Refuse rather than erase journals ++ // with pending recovery or silently expire terminal private snapshots. ++ count, err := db.GetEngine(tx).Count(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if count >= 256 { ++ return ErrSourceOperationConflict ++ } ++ active, err := db.GetEngine(tx).Where("phase IN (?, ?, ?)", "prepared", "applied", "unchanged").Count(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if active >= 64 { ++ return ErrSourceOperationConflict ++ } ++ found, err := db.GetEngine(tx).ID(operationID).Exist(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if found { ++ return ErrSourceOperationConflict ++ } ++ shared, err := db.GetEngine(tx).Where("target_name = ? AND phase IN (?, ?, ?)", desired.Name, "prepared", "applied", "unchanged").Exist(new(sourceOperationJournal)) ++ if err != nil { ++ return err ++ } ++ if shared { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if len(encodeSourceInventory(rows)) > 1024*1024 { ++ return ErrSourceOperationConflict ++ } ++ matched := false ++ for _, row := range rows { ++ if row.Name == desired.Name || row.ID == targetID { ++ if targetID == 0 || row.ID != targetID || row.Name != desired.Name || row.Type != OAuth2 { ++ return ErrSourceOperationConflict ++ } ++ matched = true ++ } ++ } ++ if targetID != 0 && !matched { ++ return ErrSourceOperationConflict ++ } ++ j := &sourceOperationJournal{OperationID: operationID, CaseID: caseID, TargetName: desired.Name, TargetID: targetID, Phase: "prepared", BeforeState: encodeSourceInventory(rows), DesiredState: string(candidateBytes)} ++ if _, err := db.GetEngine(tx).Insert(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// ApplySourceOperation atomically compares every original raw source row, ++// mutates exactly the target and persists confirmed post-state. A lost commit ++// acknowledgement is reconciled using the same operation ID, never a fresh ID. ++// No source registration/network call occurs inside the database transaction. ++func ApplySourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if j.Phase == "applied" || j.Phase == "unchanged" { ++ if encodeSourceInventory(rows) != j.PostState { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ } ++ if j.Phase != "prepared" || encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ var desired sourceOperationRow ++ if json.Unmarshal([]byte(j.DesiredState), &desired) != nil { ++ return ErrSourceOperationConflict ++ } ++ unchanged := false ++ for _, row := range rows { ++ if row.ID == j.TargetID { ++ desired.CreatedUnix = row.CreatedUnix ++ desired.UpdatedUnix = row.UpdatedUnix ++ unchanged = reflect.DeepEqual(row, desired) ++ } ++ } ++ if !unchanged { ++ desired.UpdatedUnix = timeutil.TimeStampNow() ++ if j.TargetID == 0 { ++ desired.CreatedUnix = desired.UpdatedUnix ++ if _, err := db.GetEngine(tx).NoAutoTime().Insert(&desired); err != nil { ++ return err ++ } ++ j.TargetID = desired.ID ++ } else { ++ n, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(&desired) ++ if err != nil { ++ return err ++ } ++ if n != 1 { ++ return ErrSourceOperationConflict ++ } ++ } ++ } ++ post, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ expected := append([]sourceOperationRow{}, rows...) ++ if !unchanged { ++ replaced := false ++ for i := range expected { ++ if expected[i].ID == desired.ID { ++ expected[i] = desired ++ replaced = true ++ } ++ } ++ if !replaced { ++ expected = append(expected, desired) ++ } ++ sort.Slice(expected, func(i, k int) bool { return expected[i].ID < expected[k].ID }) ++ } ++ if !reflect.DeepEqual(post, expected) { ++ return ErrSourceOperationConflict ++ } ++ j.PostState = encodeSourceInventory(post) ++ j.Phase = "applied" ++ if unchanged { ++ j.Phase = "unchanged" ++ } ++ if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// InverseSourceOperation requires fresh external inverse authority on EACH call. ++// It restores a preexisting source in place; only confirmed case creation can ++// delete. Any linked local/external account refuses deletion without alteration. ++func InverseSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if j.Phase == "reverted" || j.Phase == "cancelled" { ++ if encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ } ++ if j.Phase == "prepared" { ++ if encodeSourceInventory(rows) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ j.Phase = "cancelled" ++ } else { ++ if (j.Phase != "applied" && j.Phase != "unchanged") || encodeSourceInventory(rows) != j.PostState { ++ return ErrSourceOperationConflict ++ } ++ before, err := decodeSourceInventory(j.BeforeState) ++ if err != nil { ++ return err ++ } ++ var original *sourceOperationRow ++ for i := range before { ++ if before[i].ID == j.TargetID { ++ original = &before[i] ++ } ++ } ++ if original != nil { ++ if _, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(original); err != nil { ++ return err ++ } ++ } else { ++ for _, table := range []struct{ name, column string }{{"user", "login_source"}, {"external_login_user", "login_source_id"}} { ++ count, err := db.GetEngine(tx).Table(table.name).Where(table.column+" = ?", j.TargetID).Count() ++ if err != nil { ++ return err ++ } ++ if count != 0 { ++ return ErrSourceInUse{ID: j.TargetID} ++ } ++ } ++ n, err := db.GetEngine(tx).ID(j.TargetID).Delete(new(sourceOperationRow)) ++ if err != nil { ++ return err ++ } ++ if n != 1 { ++ return ErrSourceOperationConflict ++ } ++ } ++ restored, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ if encodeSourceInventory(restored) != j.BeforeState { ++ return ErrSourceOperationConflict ++ } ++ j.Phase = "reverted" ++ } ++ if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil { ++ return err ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} ++ ++// ReconcileSourceOperation only checks committed journal/current equality. It ++// never infers that a prepared intent ran and never retries the mutation. The ++// separately authenticated runtime must call this after an ambiguous result. ++func ReconcileSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) { ++ var result SourceOperationResult ++ err := sourceOperationTx(ctx, func(tx context.Context) error { ++ j, err := sourceJournal(tx, operationID) ++ if err != nil { ++ return err ++ } ++ if j.CaseID != caseID { ++ return ErrSourceOperationConflict ++ } ++ rows, err := sourceInventory(tx) ++ if err != nil { ++ return err ++ } ++ expected := j.BeforeState ++ switch j.Phase { ++ case "prepared", "reverted", "cancelled": ++ case "applied", "unchanged": ++ expected = j.PostState ++ default: ++ return ErrSourceOperationConflict ++ } ++ if encodeSourceInventory(rows) != expected { ++ return ErrSourceOperationConflict ++ } ++ result = journalResult(j) ++ return nil ++ }) ++ if err != nil { ++ return SourceOperationResult{}, err ++ } ++ return result, nil ++} +--- /dev/null ++++ b/models/auth/source_operation_internal_test.go +@@ -0,0 +1,87 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth ++ ++import ( ++ "context" ++ "testing" ++ "time" ++ ++ "forgejo.org/models/db" ++ "xorm.io/xorm" ++) ++ ++func TestSourceOperationDatabaseWriterExclusion(t *testing.T) { ++ // Use DEFERRED transaction mode in the managed engine as well, so this ++ // test detects removal of the primitive's explicit first write. ++ previousContext := db.DefaultContext ++ previousEngine, ok := db.GetEngine(previousContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual fixture engine") ++ } ++ managed, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred") ++ if err != nil { ++ t.Fatal(err) ++ } ++ db.SetDefaultEngine(context.Background(), managed) ++ defer func() { db.SetDefaultEngine(previousContext, previousEngine); managed.Close() }() ++ // A second actual connection executes uncooperative raw SQL, so this checks ++ // database exclusion rather than an advisory-lock convention. ++ raw, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer raw.Close() ++ locked := make(chan struct{}) ++ release := make(chan struct{}) ++ done := make(chan error, 1) ++ go func() { ++ done <- sourceOperationTx(db.DefaultContext, func(ctx context.Context) error { close(locked); <-release; return nil }) ++ }() ++ select { ++ case <-locked: ++ case err := <-done: ++ t.Fatalf("lock failed: %v", err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("lock timed out") ++ } ++ writeDone := make(chan error, 1) ++ go func() { _, err := raw.Exec("UPDATE login_source SET name = name WHERE id = -123"); writeDone <- err }() ++ select { ++ case err := <-writeDone: ++ close(release) ++ <-done ++ t.Fatalf("uncooperative write escaped exclusion: %v", err) ++ case <-time.After(100 * time.Millisecond): ++ } ++ close(release) ++ select { ++ case err := <-done: ++ if err != nil { ++ t.Fatal(err) ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("transaction did not finish") ++ } ++ select { ++ case err := <-writeDone: ++ if err != nil { ++ t.Fatal(err) ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("writer did not resume") ++ } ++} ++func TestSourceOperationRefusesActualXORMArgumentLogging(t *testing.T) { ++ engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual engine") ++ } ++ engine.ShowSQL(true) ++ defer engine.ShowSQL(false) ++ called := false ++ err := sourceOperationTx(db.DefaultContext, func(context.Context) error { called = true; return nil }) ++ if err != ErrSourceOperationUnsupported || called { ++ t.Fatal("actual SQL argument logger did not fail closed") ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_postgres_export_test.go +@@ -0,0 +1,7 @@ ++//go:build source_operation_postgres ++ ++package auth ++ ++// Test-only seam: the production transaction body still obtains its real locks ++// and private query context. No runtime API or authority callback is added. ++var SourceOperationPostgresTransactionForTest = sourceOperationTx +--- /dev/null ++++ b/models/auth/source_operation_postgres_test.go +@@ -0,0 +1,369 @@ ++//go:build source_operation_postgres ++ ++package auth_test ++ ++import ( ++ "bytes" ++ "context" ++ "errors" ++ "fmt" ++ "os" ++ "runtime/trace" ++ "strings" ++ "sync" ++ "testing" ++ "time" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ user_model "forgejo.org/models/user" ++ "forgejo.org/modules/log" ++ "forgejo.org/modules/setting" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/lib/pq" ++ "github.com/stretchr/testify/require" ++ "xorm.io/xorm" ++) ++ ++type postgresOperationLog struct { ++ log.Logger ++ mu sync.Mutex ++ entries []string ++} ++ ++func (l *postgresOperationLog) Log(_ int, _ log.Level, format string, values ...any) { ++ l.mu.Lock() ++ defer l.mu.Unlock() ++ l.entries = append(l.entries, fmt.Sprintf(format, values...)) ++} ++func (l *postgresOperationLog) take() string { ++ l.mu.Lock() ++ defer l.mu.Unlock() ++ result := strings.Join(l.entries, "\n") ++ l.entries = nil ++ return result ++} ++ ++// The real Forgejo engine/driver, registered model schemas and public operation ++// functions run. Only the database, application configuration and actors are ++// synthetic. No deployed owner database or authenticated operation is claimed. ++func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) { ++ t.Helper() ++ require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required") ++ oldSettings, oldContext := setting.Database, db.DefaultContext ++ oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine) ++ require.True(t, ok) ++ expectedSchema := schema ++ if expectedSchema == "" { ++ expectedSchema = "public" ++ } ++ raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+expectedSchema) ++ require.NoError(t, err) ++ _, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + expectedSchema + `"`) ++ require.NoError(t, err) ++ setting.Database.Type = "postgres" ++ setting.Database.Host = "/run/source-operation-postgres" ++ setting.Database.Name = "custodian_source" ++ setting.Database.User = "custodian_test" ++ setting.Database.Passwd = "" ++ setting.Database.Schema = schema ++ setting.Database.SSLMode = "disable" ++ setting.Database.LogSQL = false ++ setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2 ++ setting.Database.SlowQueryThreshold = 0 ++ require.NoError(t, db.InitEngine(context.Background())) ++ engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) ++ require.True(t, ok) ++ t.Cleanup(func() { ++ _ = engine.Close() ++ _ = raw.Close() ++ setting.Database = oldSettings ++ db.SetDefaultEngine(oldContext, oldEngine) ++ }) ++ version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema") ++ require.NoError(t, err) ++ require.Equal(t, "16", string(version[0]["major"])) ++ require.Equal(t, expectedSchema, string(version[0]["schema"])) ++ require.NoError(t, db.SyncAllTables()) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ return db.DefaultContext, engine, raw ++} ++ ++func resetPostgresOperations(t *testing.T, engine *xorm.Engine) { ++ t.Helper() ++ _, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`) ++ require.NoError(t, err) ++} ++ ++func TestSourceOperationPostgres(t *testing.T) { ++ for _, profile := range []struct{ name, schema string }{{"default", ""}, {"public", "public"}, {"named", "custodian_named"}} { ++ t.Run(profile.name, func(t *testing.T) { ++ ctx, engine, raw := postgresOperationDB(t, profile.schema) ++ run := func(name string, f func(*testing.T)) { ++ t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) }) ++ } ++ run("create-reconcile-inverse", func(t *testing.T) { ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource()) ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ require.Positive(t, r.SourceID) ++ post := operationRows(t, ctx) ++ again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, r, again) ++ require.Equal(t, post, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ }) ++ run("exact-raw-preexisting-restoration", func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" ++ _, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") ++ require.NoError(t, err) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ run("unchanged-preexisting-survives", func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") ++ require.NoError(t, err) ++ require.Equal(t, "unchanged", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ for _, stage := range []string{"before-apply", "before-inverse"} { ++ run("private-drift-"+stage, func(t *testing.T) { ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s) ++ require.NoError(t, err) ++ if stage == "before-inverse" { ++ _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ require.NoError(t, err) ++ } ++ _, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ if stage == "before-apply" { ++ _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ } else { ++ _, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") ++ } ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ }) ++ } ++ for _, kind := range []string{"user", "external"} { ++ run("linked-"+kind+"-refuses-deletion", func(t *testing.T) { ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") ++ require.NoError(t, err) ++ if kind == "user" { ++ _, err = engine.Insert(&user_model.User{ID: 1, LowerName: "synthetic", Name: "synthetic", Email: "synthetic@invalid.test", LoginSource: r.SourceID}) ++ } else { ++ _, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID) ++ } ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") ++ require.True(t, auth.IsErrSourceInUse(err)) ++ require.Equal(t, before, operationRows(t, ctx)) ++ var count int64 ++ if kind == "user" { ++ count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count() ++ } else { ++ count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count() ++ } ++ require.NoError(t, err) ++ require.EqualValues(t, 1, count) ++ }) ++ } ++ run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) { ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ }) ++ run("driver-error-rollback-and-private-diagnostics", func(t *testing.T) { ++ captured := new(postgresOperationLog) ++ engine.AddHook(&db.SlowQueryHook{Logger: captured}) ++ engine.AddHook(&db.ErrorQueryHook{Logger: captured}) ++ const control = "synthetic-postgres-unmarked-control" ++ _, err := db.GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", control) ++ require.Error(t, err) ++ require.Contains(t, captured.take(), control, "actual unmarked diagnostic control must be observable") ++ _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_fail_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '%', NEW.cfg; END $$`) ++ require.NoError(t, err) ++ _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_fail_source()`) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.PrepareSourceOperation(ctx, "postgres-error-operation", "postgres-error-case", 0, operationSource()) ++ require.NoError(t, err) ++ captured.take() ++ var tracing bytes.Buffer ++ require.NoError(t, trace.Start(&tracing)) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-error-operation", "postgres-error-case") ++ trace.Stop() ++ require.ErrorIs(t, err, auth.ErrSourceOperationStorage) ++ messages := captured.take() ++ require.Contains(t, messages, "[Error SQL Query] [owner-confined query]") ++ require.Contains(t, messages, "[Slow SQL Query] [owner-confined query]") ++ require.NotContains(t, messages, "synthetic-private-original") ++ require.False(t, bytes.Contains(tracing.Bytes(), []byte("synthetic-private-original")), "actual PostgreSQL trace disclosed private cfg") ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-error-operation", "postgres-error-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ }) ++ run("uncooperative-writers-excluded", func(t *testing.T) { ++ locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1) ++ var once sync.Once ++ releaseLock := func() { once.Do(func() { close(release) }) } ++ defer releaseLock() ++ go func() { ++ done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil }) ++ }() ++ select { ++ case <-locked: ++ case err := <-done: ++ t.Fatalf("lock acquisition refused: %v", err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("lock acquisition timed out") ++ } ++ statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`} ++ for _, statement := range statements { ++ session := raw.NewSession() ++ require.NoError(t, session.Begin()) ++ _, err := session.Exec(`SET LOCAL lock_timeout='150ms'`) ++ require.NoError(t, err) ++ _, err = session.Exec(statement) ++ var pgerr *pq.Error ++ blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03" ++ _ = session.Rollback() ++ _ = session.Close() ++ require.True(t, blocked, "an uncooperative writer escaped the actual table lock") ++ } ++ releaseLock() ++ select { ++ case err := <-done: ++ require.NoError(t, err) ++ case <-time.After(5 * time.Second): ++ t.Fatal("transaction did not finish") ++ } ++ for _, statement := range statements { ++ _, err := raw.Exec(statement) ++ require.NoError(t, err) ++ } ++ }) ++ run("concurrent-preparation-has-one-owner", func(t *testing.T) { ++ start, done := make(chan struct{}), make(chan error, 2) ++ for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} { ++ go func(id string) { ++ <-start ++ _, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource()) ++ done <- err ++ }(id) ++ } ++ close(start) ++ ok, conflict := 0, 0 ++ for range 2 { ++ select { ++ case err := <-done: ++ if err == nil { ++ ok++ ++ } else if errors.Is(err, auth.ErrSourceOperationConflict) { ++ conflict++ ++ } else { ++ t.Fatal("unexpected concurrent preparation error") ++ } ++ case <-time.After(5 * time.Second): ++ t.Fatal("concurrent preparation timed out") ++ } ++ } ++ require.Equal(t, 1, ok) ++ require.Equal(t, 1, conflict) ++ }) ++ }) ++ } ++} ++ ++func TestSourceOperationPostgresProcessRecovery(t *testing.T) { ++ step := os.Getenv("CUSTODIAN_PG_STEP") ++ require.NotEmpty(t, step, "explicit synthetic recovery phase required") ++ ctx, engine, _ := postgresOperationDB(t, "custodian_recovery") ++ switch step { ++ case "prepare": ++ resetPostgresOperations(t, engine) ++ _, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource()) ++ require.NoError(t, err) ++ case "crash": ++ engine.AddHook(operationCrashAfterSourceInsert{}) ++ _, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ t.Fatal("actual source insert did not reach the process-death hook") ++ case "recover-prepared": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ _, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ case "recover-applied": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.EqualValues(t, 1, n) ++ _, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ case "recover-inverse": ++ r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ n, err := engine.Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ default: ++ t.Fatal("unknown synthetic recovery phase") ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_process_test.go +@@ -0,0 +1,97 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth_test ++ ++import ( ++ "context" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "strings" ++ "testing" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/stretchr/testify/require" ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++ "xorm.io/xorm/names" ++) ++ ++type operationCrashAfterSourceInsert struct{} ++ ++func (operationCrashAfterSourceInsert) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { ++ return c.Ctx, nil ++} ++func (operationCrashAfterSourceInsert) AfterProcess(c *contexts.ContextHook) error { ++ if c.Err == nil && strings.Contains(c.SQL, "INSERT INTO") && strings.Contains(c.SQL, "login_source") && !strings.Contains(c.SQL, "login_source_operation") { ++ // Process death after the real INSERT, before actual ApplySourceOperation's ++ // post-read/journal update/commit. No artificial operation is substituted. ++ os.Exit(73) ++ } ++ return nil ++} ++func TestSourceOperationProcessRecovery(t *testing.T) { ++ if step := os.Getenv("SOURCE_OPERATION_CHILD_STEP"); step != "" { ++ e, err := xorm.NewEngine("sqlite3", os.Getenv("SOURCE_OPERATION_CHILD_DB")+"?_txlock=deferred") ++ require.NoError(t, err) ++ e.SetMapper(names.GonicMapper{}) ++ db.SetDefaultEngine(context.Background(), e) ++ require.NoError(t, db.SyncAllTables()) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ ctx := db.DefaultContext ++ switch step { ++ case "prepare": ++ _, err = auth.PrepareSourceOperation(ctx, "operation-process-recovery", "case-process-recovery", 0, operationSource()) ++ require.NoError(t, err) ++ case "crash": ++ e.AddHook(operationCrashAfterSourceInsert{}) ++ _, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ t.Fatal("real insert did not reach crash hook") ++ case "recover-prepared": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ n, err := db.GetEngine(ctx).Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ _, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ case "recover-applied": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ case "recover-inverse": ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ n, err := db.GetEngine(ctx).Table("login_source").Count() ++ require.NoError(t, err) ++ require.Zero(t, n) ++ default: ++ t.Fatal("unknown synthetic child step") ++ } ++ require.NoError(t, e.Close()) ++ return ++ } ++ exe, err := os.Executable() ++ require.NoError(t, err) ++ path := filepath.Join(t.TempDir(), "owner-private.sqlite") ++ for _, step := range []string{"prepare", "crash", "recover-prepared", "recover-applied", "recover-inverse"} { ++ child := exec.Command(exe, "-test.run=^TestSourceOperationProcessRecovery$", "-test.v") ++ child.Env = append(os.Environ(), "SOURCE_OPERATION_CHILD_STEP="+step, "SOURCE_OPERATION_CHILD_DB="+path) ++ output, err := child.CombinedOutput() ++ if step == "crash" { ++ exit, ok := err.(*exec.ExitError) ++ require.True(t, ok, string(output)) ++ require.Equal(t, 73, exit.ExitCode(), string(output)) ++ } else { ++ require.NoError(t, err, string(output)) ++ } ++ } ++} +--- /dev/null ++++ b/models/auth/source_operation_test.go +@@ -0,0 +1,235 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package auth_test ++ ++import ( ++ "context" ++ "encoding/json" ++ "fmt" ++ "testing" ++ ++ auth "forgejo.org/models/auth" ++ "forgejo.org/models/db" ++ "forgejo.org/models/unittest" ++ "forgejo.org/modules/setting" ++ oauth2 "forgejo.org/services/auth/source/oauth2" ++ ++ "github.com/stretchr/testify/require" ++) ++ ++func operationDB(t *testing.T) context.Context { ++ t.Helper() ++ require.NoError(t, unittest.PrepareTestDatabase()) ++ _, err := db.GetEngine(db.DefaultContext).Exec("DELETE FROM login_source_operation") ++ require.NoError(t, err) ++ auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) ++ return db.DefaultContext ++} ++func operationSource() *auth.Source { ++ return &auth.Source{Type: auth.OAuth2, Name: "operation-test", IsActive: false, Cfg: &oauth2.Source{Provider: "openidConnect", ClientID: "synthetic-client", ClientSecret: "synthetic-private-original", RequiredClaimName: "groups", RequiredClaimValues: []string{"g1", "g2", "g3"}, Scopes: []string{"openid", "profile", "groups"}}} ++} ++func operationRows(t *testing.T, ctx context.Context) string { ++ t.Helper() ++ rows, err := db.GetEngine(ctx).Query("SELECT * FROM login_source ORDER BY id") ++ require.NoError(t, err) ++ b, err := json.Marshal(rows) ++ require.NoError(t, err) ++ return string(b) ++} ++func TestSourceOperationCreateAndInverse(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ r, err := auth.PrepareSourceOperation(ctx, "operation-create", "case-create", 0, operationSource()) ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, "applied", r.Phase) ++ require.Positive(t, r.SourceID) ++ id := r.SourceID ++ post := operationRows(t, ctx) ++ r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, id, r.SourceID) ++ require.Equal(t, post, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "operation-create", "wrong-case") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ require.Equal(t, "reverted", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++ _, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create") ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++} ++func TestSourceOperationRestoresExactRawPreexisting(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ // The old/unknown field must survive restoration byte for byte, including the ++ // private synthetic secret. A typed OAuth2 decoder would silently lose it. ++ raw := `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}` ++ _, err := db.GetEngine(ctx).Exec("UPDATE login_source SET cfg = ? WHERE id = ?", raw, s.ID) ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" ++ _, err = auth.PrepareSourceOperation(ctx, "operation-restore", "case-restore", s.ID, s) ++ require.NoError(t, err) ++ _, err = auth.ApplySourceOperation(ctx, "operation-restore", "case-restore") ++ require.NoError(t, err) ++ _, err = auth.InverseSourceOperation(ctx, "operation-restore", "case-restore") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++ source, err := auth.GetSourceByID(ctx, s.ID) ++ require.NoError(t, err) ++ require.Equal(t, s.ID, source.ID) ++} ++func TestSourceOperationUnchangedNeverDeletesPreexisting(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-unchanged", "case-unchanged", s.ID, s) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-unchanged", "case-unchanged") ++ require.NoError(t, err) ++ require.Equal(t, "unchanged", r.Phase) ++ _, err = auth.InverseSourceOperation(ctx, "operation-unchanged", "case-unchanged") ++ require.NoError(t, err) ++ require.Equal(t, before, operationRows(t, ctx)) ++} ++func TestSourceOperationPrivateDriftRefuses(t *testing.T) { ++ for _, stage := range []string{"before-apply", "before-inverse"} { ++ t.Run(stage, func(t *testing.T) { ++ ctx := operationDB(t) ++ s := operationSource() ++ require.NoError(t, auth.CreateSource(ctx, s)) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-private-drift", "case-private-drift", s.ID, s) ++ require.NoError(t, err) ++ if stage == "before-inverse" { ++ _, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ require.NoError(t, err) ++ } ++ s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-drift" ++ require.NoError(t, auth.UpdateSource(ctx, s)) ++ drift := operationRows(t, ctx) ++ if stage == "before-apply" { ++ _, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ } else { ++ _, err = auth.InverseSourceOperation(ctx, "operation-private-drift", "case-private-drift") ++ } ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.NotContains(t, err.Error(), "synthetic-private") ++ require.Equal(t, drift, operationRows(t, ctx)) ++ }) ++ } ++} ++func TestSourceOperationStockCRUDDriftRefuses(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-other-writer", "case-other-writer", 0, operationSource()) ++ require.NoError(t, err) ++ other := operationSource() ++ other.Name = "unrelated-source" ++ require.NoError(t, auth.CreateSource(ctx, other)) ++ drift := operationRows(t, ctx) ++ _, err = auth.ApplySourceOperation(ctx, "operation-other-writer", "case-other-writer") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, drift, operationRows(t, ctx)) ++} ++func TestSourceOperationLinkedSourceRefusesDeletion(t *testing.T) { ++ for _, kind := range []string{"user", "external"} { ++ t.Run(kind, func(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-linked", "case-linked", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-linked", "case-linked") ++ require.NoError(t, err) ++ if kind == "user" { ++ _, err = db.GetEngine(ctx).Exec("UPDATE `user` SET login_source = ? WHERE id = 1", r.SourceID) ++ } else { ++ _, err = db.GetEngine(ctx).Exec("INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES (?, ?, ?)", "synthetic-external", 1, r.SourceID) ++ } ++ require.NoError(t, err) ++ before := operationRows(t, ctx) ++ _, err = auth.InverseSourceOperation(ctx, "operation-linked", "case-linked") ++ require.True(t, auth.IsErrSourceInUse(err)) ++ require.Equal(t, before, operationRows(t, ctx)) ++ if kind == "user" { ++ rows, e := db.GetEngine(ctx).Query("SELECT login_source FROM `user` WHERE id = 1") ++ require.NoError(t, e) ++ require.NotEqual(t, []byte("0"), rows[0]["login_source"]) ++ } else { ++ n, e := db.GetEngine(ctx).Table("external_login_user").Count() ++ require.NoError(t, e) ++ require.EqualValues(t, 1, n) ++ } ++ }) ++ } ++} ++func TestSourceOperationPreparedCancellationAndIDReuse(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ r, err := auth.InverseSourceOperation(ctx, "operation-cancelled", "case-cancelled") ++ require.NoError(t, err) ++ require.Equal(t, "cancelled", r.Phase) ++ require.Equal(t, before, operationRows(t, ctx)) ++} ++func TestSourceOperationRejectsUnsafeRuntime(t *testing.T) { ++ ctx := operationDB(t) ++ setting.Database.LogSQL = true ++ t.Cleanup(func() { setting.Database.LogSQL = false }) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-no-logging", "case-no-logging", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported) ++ setting.Database.LogSQL = false ++ err = db.WithTx(ctx, func(tx context.Context) error { ++ _, err := auth.PrepareSourceOperation(tx, "operation-nested", "case-nested", 0, operationSource()) ++ return err ++ }) ++ require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported) ++} ++func TestSourceOperationRefusesSharedManagedSource(t *testing.T) { ++ ctx := operationDB(t) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-first-case", "case-first-case", 0, operationSource()) ++ require.NoError(t, err) ++ r, err := auth.ApplySourceOperation(ctx, "operation-first-case", "case-first-case") ++ require.NoError(t, err) ++ shared := operationSource() ++ shared.ID = r.SourceID ++ _, err = auth.PrepareSourceOperation(ctx, "operation-second-case", "case-second-case", r.SourceID, shared) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++} ++func TestSourceOperationBoundsPrivateJournalCustody(t *testing.T) { ++ ctx := operationDB(t) ++ // A full unresolved-journal budget refuses new preparation, without deleting ++ // any intent or replacing private recovery data with an expiry assumption. ++ for i := 0; i < 64; i++ { ++ _, err := db.GetEngine(ctx).Exec("INSERT INTO login_source_operation (operation_id, case_id, target_name, phase) VALUES (?, ?, ?, ?)", fmt.Sprintf("bounded-operation-%03d", i), "bounded-case", fmt.Sprintf("bounded-target-%03d", i), "prepared") ++ require.NoError(t, err) ++ } ++ _, err := auth.PrepareSourceOperation(ctx, "operation-budget-full", "case-budget-full", 0, operationSource()) ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ n, err := db.GetEngine(ctx).Table("login_source_operation").Count() ++ require.NoError(t, err) ++ require.EqualValues(t, 64, n) ++} ++func TestSourceOperationRequiresExactPostState(t *testing.T) { ++ ctx := operationDB(t) ++ before := operationRows(t, ctx) ++ _, err := auth.PrepareSourceOperation(ctx, "operation-post-exact", "case-post-exact", 0, operationSource()) ++ require.NoError(t, err) ++ _, err = db.GetEngine(ctx).Exec("CREATE TRIGGER source_operation_test_drift AFTER INSERT ON login_source BEGIN UPDATE login_source SET name = 'unexpected-rewrite' WHERE id = NEW.id; END") ++ require.NoError(t, err) ++ defer db.GetEngine(ctx).Exec("DROP TRIGGER source_operation_test_drift") ++ _, err = auth.ApplySourceOperation(ctx, "operation-post-exact", "case-post-exact") ++ require.ErrorIs(t, err, auth.ErrSourceOperationConflict) ++ require.Equal(t, before, operationRows(t, ctx)) ++ r, err := auth.ReconcileSourceOperation(ctx, "operation-post-exact", "case-post-exact") ++ require.NoError(t, err) ++ require.Equal(t, "prepared", r.Phase) ++} +--- a/models/db/context.go ++++ b/models/db/context.go +@@ -72,6 +72,12 @@ + // GetEngine will get a db Engine from this context or return an Engine restricted to this context + func GetEngine(ctx context.Context) Engine { + if e := getEngine(ctx); e != nil { ++ // A private-query marker must reach XORM's real query hooks, including ++ // inside an existing engine/transaction context. Wrapping the Go ++ // context alone does not change XORM's stored session context. ++ if isSensitiveSQL(ctx) { ++ return e.Context(ctx) ++ } + return e + } + return x.Context(ctx) +--- a/models/db/engine.go ++++ b/models/db/engine.go +@@ -330,8 +330,15 @@ + func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + ctx, task := trace.NewTask(c.Ctx, "sql") + ctx = context.WithValue(ctx, sqlTask{}, task) +- trace.Log(ctx, "query", c.SQL) +- trace.Logf(ctx, "args", "%v", c.Args) ++ // XORM passes the same ContextHook to every BeforeProcess hook. Publish ++ // the task there too, so a later hook returning c.Ctx retains it. ++ c.Ctx = ctx ++ if isSensitiveSQL(c.Ctx) { ++ trace.Log(ctx, "query", "[owner-confined query]") ++ } else { ++ trace.Log(ctx, "query", c.SQL) ++ trace.Logf(ctx, "args", "%v", c.Args) ++ } + return ctx, nil + } + +@@ -353,7 +360,11 @@ + + func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.ExecuteTime >= h.Treshold { +- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) ++ if isSensitiveSQL(c.Ctx) { ++ h.Logger.Log(8, log.WARN, "[Slow SQL Query] [owner-confined query] - %v", c.ExecuteTime) ++ } else { ++ h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) ++ } + } + return nil + } +@@ -370,7 +381,11 @@ + + func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.Err != nil && !errors.Is(c.Err, context.Canceled) { +- h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) +- } +- return nil +-} ++ if isSensitiveSQL(c.Ctx) { ++ h.Logger.Log(8, log.ERROR, "[Error SQL Query] [owner-confined query]") ++ } else { ++ h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) ++ } ++ } ++ return nil ++} +--- /dev/null ++++ b/models/db/sensitive_sql.go +@@ -0,0 +1,47 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package db ++ ++import ( ++ "context" ++ "forgejo.org/modules/setting" ++ "xorm.io/xorm" ++) ++ ++type sensitiveSQLKey struct{} ++ ++// WithSensitiveSQL suppresses argument, statement and driver-error disclosure ++// in the application's query hooks. The caller must separately refuse raw XORM ++// SQL logging; custom driver/server auditing remains an owner custody concern. ++func WithSensitiveSQL(ctx context.Context) context.Context { ++ return context.WithValue(ctx, sensitiveSQLKey{}, true) ++} ++func isSensitiveSQL(ctx context.Context) bool { ++ sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool) ++ return sensitive ++} ++ ++// SQLLoggingEnabled checks the underlying engine without changing the context or ++// assuming that a sensitive context still exposes an Engine rather than Session. ++// Unknown/uninitialized backends refuse, and query redaction never permits raw ++// XORM logging. It neither creates a transaction nor closes an existing one. ++func SQLLoggingEnabled(ctx context.Context) bool { ++ if setting.Database.LogSQL { ++ return true ++ } ++ engine := getEngine(ctx) ++ if engine == nil { ++ if x == nil { ++ return true ++ } ++ engine = x ++ } ++ switch current := engine.(type) { ++ case *xorm.Engine: ++ return current == nil || current.Logger() == nil || current.Logger().IsShowSQL() ++ case *xorm.Session: ++ return current == nil || current.Engine() == nil || current.Engine().Logger() == nil || current.Engine().Logger().IsShowSQL() ++ default: ++ return true ++ } ++} +--- /dev/null ++++ b/models/db/sensitive_sql_logging_test.go +@@ -0,0 +1,41 @@ ++package db ++ ++import ( ++ "context" ++ "testing" ++ ++ "forgejo.org/modules/setting" ++ "xorm.io/xorm" ++) ++ ++func TestSensitiveSQLLoggingChecksUnderlyingEngine(t *testing.T) { ++ engine, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer engine.Close() ++ engine.SetLogger(NewXORMLogger(false)) ++ session := engine.NewSession() ++ defer session.Close() ++ previous := setting.Database.LogSQL ++ defer func() { setting.Database.LogSQL = previous }() ++ for name, backend := range map[string]Engine{"engine": engine, "session": session} { ++ t.Run(name, func(t *testing.T) { ++ ctx := WithSensitiveSQL(newContext(context.Background(), backend, false)) ++ setting.Database.LogSQL = false ++ engine.ShowSQL(false) ++ if SQLLoggingEnabled(ctx) { ++ t.Fatal("private context with disabled actual SQL logger refused") ++ } ++ engine.ShowSQL(true) ++ if !SQLLoggingEnabled(ctx) { ++ t.Fatal("actual raw SQL logger escaped refusal") ++ } ++ engine.ShowSQL(false) ++ setting.Database.LogSQL = true ++ if !SQLLoggingEnabled(ctx) { ++ t.Fatal("configured raw SQL logger escaped refusal") ++ } ++ }) ++ } ++} +--- /dev/null ++++ b/models/db/sensitive_sql_test.go +@@ -0,0 +1,103 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package db ++ ++import ( ++ "bytes" ++ "context" ++ "errors" ++ "fmt" ++ "runtime/trace" ++ "strings" ++ "testing" ++ "time" ++ ++ "forgejo.org/modules/log" ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++) ++ ++type sensitiveSQLLog struct { ++ log.Logger ++ entries []string ++} ++ ++func (l *sensitiveSQLLog) Log(_ int, _ log.Level, f string, v ...any) { ++ l.entries = append(l.entries, fmt.Sprintf(f, v...)) ++} ++func TestSensitiveSQLHooks(t *testing.T) { ++ const secret = "synthetic-query-private-sentinel" ++ for _, sensitive := range []bool{false, true} { ++ ctx := context.Background() ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ h := &contexts.ContextHook{Ctx: ctx, SQL: "SELECT '" + secret + "'", Args: []any{secret}, Err: errors.New(secret), ExecuteTime: time.Second} ++ logger := new(sensitiveSQLLog) ++ if err := (&SlowQueryHook{Logger: logger}).AfterProcess(h); err != nil { ++ t.Fatal(err) ++ } ++ if err := (&ErrorQueryHook{Logger: logger}).AfterProcess(h); err != nil { ++ t.Fatal(err) ++ } ++ found := strings.Contains(strings.Join(logger.entries, "\n"), secret) ++ if found == sensitive { ++ t.Fatal("query hook disclosure/redaction mismatch") ++ } ++ var out bytes.Buffer ++ if err := trace.Start(&out); err != nil { ++ t.Fatal(err) ++ } ++ next, err := (TracingHook{}).BeforeProcess(h) ++ if err != nil { ++ trace.Stop() ++ t.Fatal(err) ++ } ++ h.Ctx = next ++ err = (TracingHook{}).AfterProcess(h) ++ trace.Stop() ++ if err != nil { ++ t.Fatal(err) ++ } ++ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive { ++ t.Fatal("trace disclosure/redaction mismatch") ++ } ++ } ++} ++ ++func TestSensitiveSQLActualContextPropagation(t *testing.T) { ++ engine, ok := GetEngine(DefaultContext).(*xorm.Engine) ++ if !ok { ++ t.Fatal("missing actual fixture engine") ++ } ++ logger := new(sensitiveSQLLog) ++ engine.AddHook(&SlowQueryHook{Logger: logger}) ++ engine.AddHook(&ErrorQueryHook{Logger: logger}) ++ engine.AddHook(TracingHook{}) ++ const secret = "synthetic-actual-xorm-private-sentinel" ++ for _, sensitive := range []bool{false, true} { ++ logger.entries = nil ++ ctx := context.Context(DefaultContext) ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ var out bytes.Buffer ++ if err := trace.Start(&out); err != nil { ++ t.Fatal(err) ++ } ++ _, err := GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", secret) ++ trace.Stop() ++ if err == nil { ++ t.Fatal("expected actual SQLite function refusal") ++ } ++ if len(logger.entries) < 2 { ++ t.Fatal("actual query hooks did not execute") ++ } ++ if strings.Contains(strings.Join(logger.entries, "\n"), secret) == sensitive { ++ t.Fatal("actual XORM query context did not preserve redaction") ++ } ++ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive { ++ t.Fatal("actual XORM trace context did not preserve redaction") ++ } ++ } ++} +--- /dev/null ++++ b/models/db/tracing_context_test.go +@@ -0,0 +1,54 @@ ++package db ++ ++import ( ++ "bytes" ++ "context" ++ "runtime/trace" ++ "testing" ++ ++ "xorm.io/xorm" ++ "xorm.io/xorm/contexts" ++) ++ ++type followingQueryHook struct{} ++ ++func (followingQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { ++ return c.Ctx, nil ++} ++func (followingQueryHook) AfterProcess(*contexts.ContextHook) error { return nil } ++ ++func TestTracingContextSurvivesFollowingHooks(t *testing.T) { ++ for _, sensitive := range []bool{false, true} { ++ for _, fail := range []bool{false, true} { ++ engine, err := xorm.NewEngine("sqlite3", ":memory:") ++ if err != nil { ++ t.Fatal(err) ++ } ++ engine.AddHook(TracingHook{}) ++ engine.AddHook(followingQueryHook{}) ++ ctx := context.Background() ++ if sensitive { ++ ctx = WithSensitiveSQL(ctx) ++ } ++ var output bytes.Buffer ++ if err := trace.Start(&output); err != nil { ++ t.Fatal(err) ++ } ++ func() { ++ defer trace.Stop() ++ query := "SELECT ?" ++ if fail { ++ query = "SELECT missing_synthetic_function(?)" ++ } ++ _, err = engine.Context(ctx).Exec(query, "synthetic-following-hook-sentinel") ++ }() ++ _ = engine.Close() ++ if (err != nil) != fail { ++ t.Fatal("unexpected real SQLite query outcome") ++ } ++ if bytes.Contains(output.Bytes(), []byte("synthetic-following-hook-sentinel")) == sensitive { ++ t.Fatal("following hook changed private or ordinary trace disclosure") ++ } ++ } ++ } ++} +--- a/routers/web/admin/auths.go ++++ b/routers/web/admin/auths.go +@@ -322,6 +322,13 @@ + ctx.Data["Source"] = source + ctx.Data["HasTLS"] = source.HasTLS() + ++ // The legacy form has no representation for the opt-in current-UserInfo gate. ++ // Refuse rather than silently dropping it during source reconstruction. ++ if current, ok := source.Cfg.(*oauth2.Source); ok && current.RequiredClaimValues != nil { ++ ctx.RenderWithErr("This OAuth source uses required-claim-values; update it through the reviewed CLI.", tplAuthEdit, form) ++ return ++ } ++ + if ctx.HasError() { + ctx.HTML(http.StatusOK, tplAuthEdit) + return +--- a/routers/web/auth/oauth.go ++++ b/routers/web/auth/oauth.go +@@ -1427,7 +1427,13 @@ + return goth.User{}, err + } + +- if oauth2Source.RequiredClaimName != "" { ++ if oauth2Source.RequiredClaimValues != nil { ++ // The exact callback has already authenticated and enforced the current ++ // Access Engine application.access decision. Group policy lives there. ++ if oauth2Source.Provider != "openidConnect" { ++ return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} ++ } ++ } else if oauth2Source.RequiredClaimName != "" { + claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName] + if !has { + return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine.go +@@ -0,0 +1,224 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package oauth2 ++ ++import ( ++ "context" ++ "crypto" ++ "crypto/x509" ++ "encoding/json" ++ "encoding/pem" ++ "errors" ++ "io" ++ "os" ++ "path/filepath" ++ "strings" ++ "time" ++ ++ "github.com/golang-jwt/jwt/v5" ++ "github.com/markbates/goth" ++ "github.com/netkingdom/access-engine/pkg/api" ++ "github.com/netkingdom/access-engine/pkg/decisionpep" ++) ++ ++var errApplicationDecision = errors.New("current authenticated application access refused") ++ ++// AccessEngineAdmission contains owner-admitted public trust, with only a file ++// reference to the private PDP caller token. It is not persisted OAuth source ++// configuration and cannot be replaced by token claims or provider discovery. ++type AccessEngineAdmission struct { ++ SourceName string `json:"source_name"` ++ Issuer string `json:"issuer"` ++ Audience string `json:"audience"` ++ TokenType string `json:"token_type"` ++ Algorithms []string `json:"algorithms"` ++ IdentityKeys map[string]string `json:"identity_keys"` ++ TenantClaim string `json:"tenant_claim"` ++ PrincipalTypeClaim string `json:"principal_type_claim"` ++ AssuranceClaim string `json:"assurance_claim"` ++ TenantSourceClaim string `json:"tenant_source_claim"` ++ MaxAuthenticationAge string `json:"max_authentication_age"` ++ MaxIdentityAge string `json:"max_identity_age"` ++ Decision decisionpep.Admission `json:"decision"` ++} ++ ++// VerifiedApplicationIdentity is derived only by VerifyApplicationIdentity from ++// independently admitted JOSE keys. RawData and UserInfo never supply these ++// fields. This snapshot is an identity fact, never an authorization verdict. ++type VerifiedApplicationIdentity struct { ++ Issuer string ++ Subject string ++ Tenant string ++ PrincipalType api.SubjectType ++ Assurance string ++ TenantSource string ++ AssuranceData map[string]any ++ AuthenticatedAt time.Time ++ NotAfter time.Time ++} ++ ++func loadAccessEngineAdmission(path string) (AccessEngineAdmission, error) { ++ var a AccessEngineAdmission ++ if !filepath.IsAbs(path) { ++ return a, errApplicationDecision ++ } ++ f, e := os.Open(path) ++ if e != nil { ++ return a, errApplicationDecision ++ } ++ defer f.Close() ++ stat, e := f.Stat() ++ if e != nil || !stat.Mode().IsRegular() || stat.Mode().Perm()&0022 != 0 { ++ return a, errApplicationDecision ++ } ++ raw, e := io.ReadAll(io.LimitReader(f, 1<<20+1)) ++ if e != nil || len(raw) > 1<<20 { ++ return a, errApplicationDecision ++ } ++ d := json.NewDecoder(strings.NewReader(string(raw))) ++ d.DisallowUnknownFields() ++ if d.Decode(&a) != nil { ++ return a, errApplicationDecision ++ } ++ if _, e = d.Token(); e != io.EOF { ++ return a, errApplicationDecision ++ } ++ return a, nil ++} ++ ++// VerifyApplicationIdentity validates the ID token independently of Goth's ++// decode/claim checks and of the UserInfo merge. Keys and accepted algorithm, ++// issuer, audience and type are admitted separately; none come from the JWT URL. ++func VerifyApplicationIdentity(raw string, a AccessEngineAdmission, now time.Time) (VerifiedApplicationIdentity, error) { ++ var out VerifiedApplicationIdentity ++ age, e := time.ParseDuration(a.MaxIdentityAge) ++ authAge, authErr := time.ParseDuration(a.MaxAuthenticationAge) ++ if e != nil || authErr != nil || authAge <= 0 || age <= 0 || a.TenantSourceClaim == "" || a.Issuer == "" || a.Audience == "" || a.TokenType == "" || len(a.Algorithms) == 0 || len(a.IdentityKeys) == 0 || a.TenantClaim == "" || a.PrincipalTypeClaim == "" || a.AssuranceClaim == "" { ++ return out, errApplicationDecision ++ } ++ for _, alg := range a.Algorithms { ++ if alg != "RS256" && alg != "RS384" && alg != "RS512" && alg != "ES256" && alg != "ES384" && alg != "ES512" && alg != "EdDSA" { ++ return out, errApplicationDecision ++ } ++ } ++ claims := jwt.MapClaims{} ++ token, e := jwt.ParseWithClaims(raw, claims, func(t *jwt.Token) (any, error) { ++ if typ, ok := t.Header["typ"].(string); !ok || typ != a.TokenType { ++ return nil, errApplicationDecision ++ } ++ kid, ok := t.Header["kid"].(string) ++ if !ok || kid == "" { ++ return nil, errApplicationDecision ++ } ++ text, ok := a.IdentityKeys[kid] ++ if !ok { ++ return nil, errApplicationDecision ++ } ++ block, rest := pem.Decode([]byte(text)) ++ if block == nil || block.Type != "PUBLIC KEY" || len(strings.TrimSpace(string(rest))) != 0 { ++ return nil, errApplicationDecision ++ } ++ key, e := x509.ParsePKIXPublicKey(block.Bytes) ++ if e != nil { ++ return nil, errApplicationDecision ++ } ++ var pub crypto.PublicKey = key ++ return pub, nil ++ }, jwt.WithValidMethods(a.Algorithms), jwt.WithIssuer(a.Issuer), jwt.WithAudience(a.Audience), jwt.WithExpirationRequired(), jwt.WithIssuedAt(), jwt.WithJSONNumber(), jwt.WithTimeFunc(func() time.Time { return now })) ++ if e != nil || token == nil || !token.Valid { ++ return out, errApplicationDecision ++ } ++ subject, e := claims.GetSubject() ++ if e != nil || subject == "" { ++ return out, errApplicationDecision ++ } ++ issued, e := claims.GetIssuedAt() ++ if e != nil || issued == nil || issued.Time.After(now) || now.Sub(issued.Time) > age { ++ return out, errApplicationDecision ++ } ++ end, e := claims.GetExpirationTime() ++ if e != nil || end == nil || !now.Before(end.Time) { ++ return out, errApplicationDecision ++ } ++ aud, e := claims.GetAudience() ++ if e != nil { ++ return out, errApplicationDecision ++ } ++ if authorizedParty, present := claims["azp"]; present { ++ if azp, ok := authorizedParty.(string); !ok || azp != a.Audience { ++ return out, errApplicationDecision ++ } ++ } else if len(aud) > 1 { ++ return out, errApplicationDecision ++ } ++ value := func(name string) (string, bool) { v, ok := claims[name].(string); return v, ok && validClaimLiteral(v) } ++ tenant, ok := value(a.TenantClaim) ++ if !ok { ++ return out, errApplicationDecision ++ } ++ principal, ok := value(a.PrincipalTypeClaim) ++ if !ok { ++ return out, errApplicationDecision ++ } ++ tenantSource, ok := value(a.TenantSourceClaim) ++ if !ok || (tenantSource != "directory" && tenantSource != "registration" && tenantSource != "default") { ++ return out, errApplicationDecision ++ } ++ assurance, assuranceData, authenticatedAt, authenticationEnd, err := verifiedKeyCapeAssurance(claims[a.AssuranceClaim], issued.Time, now, authAge) ++ if err != nil { ++ return out, errApplicationDecision ++ } ++ validUntil := end.Time ++ for _, bound := range []time.Time{issued.Time.Add(age), authenticationEnd} { ++ if bound.Before(validUntil) { ++ validUntil = bound ++ } ++ } ++ if !now.Before(validUntil) { ++ return out, errApplicationDecision ++ } ++ return VerifiedApplicationIdentity{Issuer: a.Issuer, Subject: subject, Tenant: tenant, PrincipalType: api.SubjectType(principal), Assurance: assurance, TenantSource: tenantSource, AssuranceData: assuranceData, AuthenticatedAt: authenticatedAt, NotAfter: validUntil}, nil ++} ++ ++func (source *Source) authorizeAccessEngine(ctx context.Context, user goth.User) error { ++ // The guarded target extension cannot silently fall back to a local group ++ // decision when admission, keys, facts, PDP or caller authentication is absent. ++ if source.Provider != "openidConnect" || ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues) != nil { ++ return errApplicationDecision ++ } ++ a, e := loadAccessEngineAdmission(os.Getenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE")) ++ if e != nil || source.authSource == nil || a.SourceName != source.authSource.Name || a.Audience != source.ClientID { ++ return errApplicationDecision ++ } ++ identity, e := VerifyApplicationIdentity(user.IDToken, a, time.Now().UTC()) ++ if e != nil { ++ return errApplicationDecision ++ } ++ // RequireCurrentUserInfoClaim has refused missing/malformed current groups ++ // before Goth can merge. The validated ID subject must still match that join. ++ sub, ok := user.RawData["sub"].(string) ++ if !ok || sub != identity.Subject || user.UserID != identity.Subject { ++ return errApplicationDecision ++ } ++ groups, ok := strictClaimValues(user.RawData[source.RequiredClaimName]) ++ if !ok { ++ return errApplicationDecision ++ } ++ client, e := decisionpep.New(a.Decision) ++ if e != nil { ++ return errApplicationDecision ++ } ++ request := api.CheckRequest{Tenant: identity.Tenant, ++ Subject: api.SubjectRef{ID: identity.Subject, Type: identity.PrincipalType, Tenant: identity.Tenant, ++ Attributes: map[string]any{"issuer": identity.Issuer, "assurance": identity.Assurance, "current_userinfo_groups": groups}}, ++ Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: identity.Tenant}, ++ Context: map[string]any{"identity_issuer": identity.Issuer, "identity_subject": identity.Subject, "identity_tenant": identity.Tenant, "identity_principal_type": string(identity.PrincipalType), "identity_assurance": identity.Assurance, "identity_assurance_claim": identity.AssuranceData, "identity_authenticated_at": identity.AuthenticatedAt.UTC().Format(time.RFC3339Nano), "identity_tenant_source": identity.TenantSource, "identity_not_after": identity.NotAfter.UTC().Format(time.RFC3339Nano), "current_userinfo_groups": groups, "required_groups_any_of": source.RequiredClaimValues, "source_name": a.SourceName, "source_id": source.authSource.ID}} ++ if _, e = client.Check(ctx, request); e != nil { ++ return errApplicationDecision ++ } ++ if !time.Now().UTC().Before(identity.NotAfter) { ++ return errApplicationDecision ++ } ++ return nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_assurance.go +@@ -0,0 +1,54 @@ ++package oauth2 ++ ++import ( ++ "encoding/json" ++ "time" ++) ++ ++// Validate the actual IAM v0.3 object only after issuer JOSE verification. These ++// are identity facts, not an access verdict: aal1/service facts still reach the ++// PDP, which owns the application's assurance and principal policy. Preserve the ++// full object and its integer timestamp in the signed decision request. ++func verifiedKeyCapeAssurance(raw any, issued, now time.Time, maxAge time.Duration) (string, map[string]any, time.Time, time.Time, error) { ++ refuse := func() (string, map[string]any, time.Time, time.Time, error) { ++ return "", nil, time.Time{}, time.Time{}, errApplicationDecision ++ } ++ claim, ok := raw.(map[string]any) ++ if !ok || len(claim) != 5 { ++ return refuse() ++ } ++ level, ok := claim["level"].(string) ++ if !ok || (level != "aal1" && level != "aal2" && level != "service") { ++ return refuse() ++ } ++ mfa, ok := claim["mfa"].(bool) ++ if !ok || mfa != (level == "aal2") || claim["source"] != "key-cape" { ++ return refuse() ++ } ++ methods, ok := claim["methods"].([]any) ++ if !ok || len(methods) == 0 { ++ return refuse() ++ } ++ seen := map[string]bool{} ++ for _, method := range methods { ++ value, ok := method.(string) ++ if !ok || !validClaimLiteral(value) || seen[value] { ++ return refuse() ++ } ++ seen[value] = true ++ } ++ number, ok := claim["at"].(json.Number) ++ if !ok { ++ return refuse() ++ } ++ seconds, err := number.Int64() ++ if err != nil || seconds <= 0 || seconds > issued.Unix() || seconds > now.Unix() { ++ return refuse() ++ } ++ authenticated := time.Unix(seconds, 0).UTC() ++ end := authenticated.Add(maxAge) ++ if maxAge <= 0 || !now.Before(end) { ++ return refuse() ++ } ++ return level, claim, authenticated, end, nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_assurance_test.go +@@ -0,0 +1,75 @@ ++package oauth2 ++ ++import ( ++ "encoding/json" ++ "testing" ++ "time" ++) ++ ++func TestAccessEngineKeyCapeAssuranceRefusals(t *testing.T) { ++ cases := map[string]func(map[string]any, time.Time){ ++ "stale-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(-10 * time.Minute).Unix() }, ++ "future-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(time.Minute).Unix() }, ++ "missing-at": func(c map[string]any, _ time.Time) { delete(c, "at") }, ++ "string-at": func(c map[string]any, _ time.Time) { c["at"] = "recent" }, ++ "fractional-at": func(c map[string]any, now time.Time) { c["at"] = float64(now.Unix()) - 1.5 }, ++ "integer-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = 1 }, ++ "inconsistent-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = false }, ++ "wrong-source": func(c map[string]any, _ time.Time) { c["source"] = "userinfo" }, ++ "wrong-level": func(c map[string]any, _ time.Time) { c["level"] = "verified" }, ++ "object-methods": func(c map[string]any, _ time.Time) { c["methods"] = map[string]any{"pwd": true} }, ++ "empty-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{} }, ++ "mixed-methods": func(c map[string]any, _ time.Time) { c["methods"] = []any{"pwd", 1} }, ++ "duplicate-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{"pwd", "pwd"} }, ++ "unknown-object-field": func(c map[string]any, _ time.Time) { c["verified"] = true }, ++ } ++ for name, mutate := range cases { ++ t.Run(name, func(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ mutate(claims["assurance"].(map[string]any), now) ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil { ++ t.Fatal("malformed or stale issuer assurance accepted") ++ } ++ }) ++ } ++ for _, source := range []string{"", "assumed"} { ++ admission, key, claims, now := identityFixture(t) ++ claims["tenant_source"] = source ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil { ++ t.Fatal("missing or unknown tenant provenance accepted") ++ } ++ } ++} ++ ++func TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ admission.MaxAuthenticationAge = "5s" ++ claims["assurance"].(map[string]any)["at"] = now.Add(-3 * time.Second).Unix() ++ identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if !identity.NotAfter.Equal(now.Add(2*time.Second)) || !identity.AuthenticatedAt.Equal(now.Add(-3*time.Second)) || identity.TenantSource != "directory" { ++ t.Fatal("snapshot overstated authentication freshness or tenant provenance") ++ } ++ if _, ok := identity.AssuranceData["at"].(json.Number); !ok || identity.AssuranceData["mfa"] != true || identity.AssuranceData["source"] != "key-cape" { ++ t.Fatal("source assurance object lost its actual wire types") ++ } ++ admission.MaxIdentityAge = "2s" ++ identity, err = VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil || !identity.NotAfter.Equal(now.Add(time.Second)) { ++ t.Fatal("snapshot overstated token-age validity", err) ++ } ++} ++ ++func TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ claim := claims["assurance"].(map[string]any) ++ claim["level"] = "aal1" ++ claim["mfa"] = false ++ claim["methods"] = []string{"pwd"} ++ identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now) ++ if err != nil || identity.Assurance != "aal1" || identity.AssuranceData["mfa"] != false { ++ t.Fatal("identity parser substituted an application authorization policy", err) ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_azp_test.go +@@ -0,0 +1,34 @@ ++package oauth2 ++ ++import "testing" ++ ++func TestAccessEngineAuthorizedPartyRefusesAnotherClient(t *testing.T) { ++ for name, value := range map[string]any{ ++ "other-client": "other-client", ++ "number": 17, ++ "array": []string{"forgejo"}, ++ "empty": "", ++ } { ++ t.Run(name, func(t *testing.T) { ++ admission, key, claims, now := identityFixture(t) ++ claims["azp"] = value ++ token := signedIdentity(t, key, claims, nil) ++ if _, err := VerifyApplicationIdentity(token, admission, now); err == nil { ++ t.Fatal("a valid signature and audience concealed an invalid authorized party") ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineAuthorizedPartyAcceptsAdmittedClient(t *testing.T) { ++ for _, multiple := range []bool{false, true} { ++ admission, key, claims, now := identityFixture(t) ++ claims["azp"] = admission.Audience ++ if multiple { ++ claims["aud"] = []string{admission.Audience, "another-resource"} ++ } ++ if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err != nil { ++ t.Fatal("admitted authorized party refused", err) ++ } ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_callback_test.go +@@ -0,0 +1,194 @@ ++package oauth2 ++ ++import ( ++ "crypto/ed25519" ++ "crypto/rand" ++ "encoding/base64" ++ "encoding/json" ++ "encoding/pem" ++ "net/http" ++ "net/http/httptest" ++ "os" ++ "path/filepath" ++ "reflect" ++ "strings" ++ "sync/atomic" ++ "testing" ++ "time" ++ ++ authmodel "forgejo.org/models/auth" ++ "github.com/golang-jwt/jwt/v5" ++ "github.com/markbates/goth" ++ "github.com/markbates/goth/gothic" ++ "github.com/netkingdom/access-engine/pkg/api" ++ "github.com/netkingdom/access-engine/pkg/decisionpep" ++ "github.com/netkingdom/access-engine/testsupport/signfixture" ++) ++ ++// This fixture substitutes only Goth's completed authentication result. The ++// actual Source.Callback, JOSE verifier, HTTPS PEP client and producer signature ++// implementation execute. Actual issuer login and native admission are separate. ++func callbackFixture(t *testing.T, group, mode string) (*Source, *goth.User, *atomic.Int64) { ++ return callbackFixtureWithIdentity(t, group, mode, nil, "") ++} ++ ++func callbackFixtureWithIdentity(t *testing.T, group, mode string, suppliedAdmission *AccessEngineAdmission, suppliedToken string) (*Source, *goth.User, *atomic.Int64) { ++ t.Helper() ++ admission, identityKey, claims, _ := identityFixture(t) ++ identityToken := signedIdentity(t, identityKey, claims, nil) ++ if suppliedAdmission != nil { ++ admission, identityToken = *suppliedAdmission, suppliedToken ++ } ++ verified, err := VerifyApplicationIdentity(identityToken, admission, time.Now().UTC()) ++ if err != nil { ++ t.Fatal("callback fixture identity invalid") ++ } ++ pub, key, err := ed25519.GenerateKey(rand.Reader) ++ if err != nil { ++ t.Fatal(err) ++ } ++ calls := new(atomic.Int64) ++ server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ calls.Add(1) ++ if r.URL.Path != "/v1/check" || r.Method != http.MethodPost || r.Header.Get("Authorization") != "Bearer synthetic-caller" || r.Header.Get("Access-Engine-JSON-Profile") != decisionpep.JSONProfile { ++ t.Error("incorrect authenticated PDP request") ++ w.WriteHeader(403) ++ return ++ } ++ var request api.CheckRequest ++ decoder := json.NewDecoder(r.Body) ++ decoder.UseNumber() ++ if err := decoder.Decode(&request); err != nil { ++ t.Error(err) ++ w.WriteHeader(400) ++ return ++ } ++ c := request.Context ++ if request.Subject.ID != "user:synthetic" || request.Subject.Tenant != "tenant:synthetic" || request.Subject.Type != "human" || request.Action != "application.access" || request.Resource.ID != "application:forgejo" || c["identity_issuer"] != admission.Issuer || c["identity_assurance"] != "aal2" || c["identity_tenant"] != "tenant:synthetic" || c["source_id"] != json.Number("17") || !reflect.DeepEqual(c["identity_assurance_claim"], verified.AssuranceData) || c["identity_authenticated_at"] != verified.AuthenticatedAt.UTC().Format(time.RFC3339Nano) || c["identity_tenant_source"] != verified.TenantSource { ++ t.Error("PDP request did not use authenticated identity and source") ++ w.WriteHeader(400) ++ return ++ } ++ now := time.Now().UTC() ++ envelope := api.DecisionEnvelope{ID: "synthetic-decision", RequestID: request.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: "v1", Subject: request.Subject, Resource: request.Resource, Binding: api.NewDecisionBindingFor(request, request), Provenance: api.DecisionProvenance{Evaluator: "flex-auth/local", Mode: "standalone/" + decisionpep.JSONProfile, PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyPackageDigest: "sha256:" + strings.Repeat("1", 64), RegistrySnapshotDigest: "sha256:" + strings.Repeat("2", 64), Caller: &api.CallerProvenance{Mode: "enforce", Principal: "system:serviceaccount:synthetic:forgejo", Audience: "access-engine", NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}} ++ api.CompleteDecision(&envelope, request, api.DecisionCompletion{AllowTTL: "10s", Now: now}) ++ switch mode { ++ case "deny": ++ envelope.Effect = api.DecisionEffectDeny ++ case "wrong-binding": ++ envelope.Binding.Subject.ID = "other" ++ case "wrong-pin": ++ envelope.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64) ++ case "expired": ++ envelope.Lifetime.ExpiresAt = now.Add(-time.Hour).Format(time.RFC3339Nano) ++ } ++ if mode != "unsigned" { ++ signingKey := key ++ if mode == "wrong-key" { ++ _, signingKey, err = ed25519.GenerateKey(rand.Reader) ++ if err != nil { ++ t.Error(err) ++ return ++ } ++ } ++ if err := signfixture.Sign(&envelope, signingKey, "synthetic-pdp"); err != nil { ++ t.Error(err) ++ return ++ } ++ } ++ w.Header().Set("Content-Type", "application/json") ++ if err := json.NewEncoder(w).Encode(envelope); err != nil { ++ t.Error(err) ++ } ++ })) ++ t.Cleanup(server.Close) ++ dir := t.TempDir() ++ ca := filepath.Join(dir, "ca.pem") ++ token := filepath.Join(dir, "caller") ++ config := filepath.Join(dir, "admission.json") ++ if err := os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0600); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(token, []byte("synthetic-caller"), 0600); err != nil { ++ t.Fatal(err) ++ } ++ admission.SourceName = "synthetic-source" ++ admission.Decision = decisionpep.Admission{Endpoint: server.URL + "/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"synthetic-pdp": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:synthetic:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "2s"} ++ raw, err := json.Marshal(admission) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if err = os.WriteFile(config, raw, 0600); err != nil { ++ t.Fatal(err) ++ } ++ t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", config) ++ source := &Source{Provider: "openidConnect", ClientID: admission.Audience, RequiredClaimName: "groups", RequiredClaimValues: []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}} ++ source.authSource = &authmodel.Source{ID: 17, Name: admission.SourceName} ++ user := &goth.User{UserID: "user:synthetic", IDToken: identityToken, RawData: map[string]any{"sub": "user:synthetic", "groups": []string{group}}} ++ previous := gothic.CompleteUserAuth ++ gothic.CompleteUserAuth = func(http.ResponseWriter, *http.Request) (goth.User, error) { return *user, nil } ++ t.Cleanup(func() { gothic.CompleteUserAuth = previous }) ++ return source, user, calls ++} ++ ++func TestAccessEngineActualCallbackAllowsAuthenticatedDecision(t *testing.T) { ++ for _, group := range []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} { ++ t.Run(group, func(t *testing.T) { ++ source, user, calls := callbackFixture(t, group, "allow") ++ // Mutable UserInfo cannot supply identity assurance, issuer or tenant. ++ user.RawData["acr"] = "aal1" ++ user.RawData["assurance"] = map[string]any{"level": "aal1", "source": "attacker"} ++ user.RawData["tenant_source"] = "attacker" ++ user.RawData["tenant"] = "tenant:attacker" ++ user.RawData["iss"] = "https://attacker.invalid" ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err != nil || got.UserID != user.UserID || calls.Load() != 1 { ++ t.Fatalf("callback result: err=%v requests=%d", err, calls.Load()) ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineActualCallbackRefusesUnverifiedPDP(t *testing.T) { ++ for _, mode := range []string{"deny", "unsigned", "wrong-key", "wrong-pin", "wrong-binding", "expired"} { ++ t.Run(mode, func(t *testing.T) { ++ source, _, calls := callbackFixture(t, "forge-admins", mode) ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err == nil || got.UserID != "" || got.IDToken != "" || calls.Load() != 1 { ++ t.Fatalf("unverified decision escaped callback: requests=%d err=%v", calls.Load(), err) ++ } ++ }) ++ } ++} ++ ++func TestAccessEngineActualCallbackChecksIdentityBeforePDP(t *testing.T) { ++ cases := map[string]func(*Source, *goth.User){ ++ "missing-token": func(_ *Source, u *goth.User) { u.IDToken = "" }, ++ "mismatched-user": func(_ *Source, u *goth.User) { u.UserID = "other" }, ++ "mismatched-subject": func(_ *Source, u *goth.User) { u.RawData["sub"] = "other" }, ++ "missing-groups": func(_ *Source, u *goth.User) { delete(u.RawData, "groups") }, ++ "wrong-source": func(s *Source, _ *goth.User) { s.authSource.Name = "other" }, ++ "wrong-provider": func(s *Source, _ *goth.User) { s.Provider = "other" }, ++ "wrong-audience": func(s *Source, _ *goth.User) { s.ClientID = "other" }, ++ "missing-admission": func(_ *Source, _ *goth.User) { t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", "") }, ++ "expired-token": func(_ *Source, u *goth.User) { ++ _, key, claims, now := identityFixture(t) ++ claims["exp"] = now.Add(-time.Hour).Unix() ++ u.IDToken = signedIdentity(t, key, claims, nil) ++ }, ++ "untrusted-identity": func(_ *Source, u *goth.User) { ++ token := jwt.NewWithClaims(jwt.SigningMethodNone, jwt.MapClaims{"sub": "user:synthetic"}) ++ u.IDToken, _ = token.SignedString(jwt.UnsafeAllowNoneSignatureType) ++ }, ++ } ++ for name, mutate := range cases { ++ t.Run(name, func(t *testing.T) { ++ source, user, calls := callbackFixture(t, "forge-admins", "allow") ++ mutate(source, user) ++ got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "") ++ if err == nil || got.UserID != "" || calls.Load() != 0 { ++ t.Fatalf("unverified identity reached PDP: requests=%d err=%v", calls.Load(), err) ++ } ++ }) ++ } ++} +--- /dev/null ++++ b/services/auth/source/oauth2/access_engine_test.go +@@ -0,0 +1,100 @@ ++package oauth2 ++ ++import ( ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/x509" ++ "encoding/pem" ++ "testing" ++ "time" ++ ++ "github.com/golang-jwt/jwt/v5" ++) ++ ++func identityFixture(t *testing.T) (AccessEngineAdmission, ed25519.PrivateKey, jwt.MapClaims, time.Time) { ++ t.Helper() ++ pub, key, e := ed25519.GenerateKey(rand.Reader) ++ if e != nil { ++ t.Fatal(e) ++ } ++ der, e := x509.MarshalPKIXPublicKey(pub) ++ if e != nil { ++ t.Fatal(e) ++ } ++ now := time.Now().UTC().Truncate(time.Second) ++ a := AccessEngineAdmission{Issuer: "https://issuer.invalid", Audience: "forgejo", TokenType: "JWT", Algorithms: []string{"EdDSA"}, IdentityKeys: map[string]string{"synthetic": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))}, TenantClaim: "tenant", PrincipalTypeClaim: "principal_type", AssuranceClaim: "assurance", TenantSourceClaim: "tenant_source", MaxAuthenticationAge: "5m", MaxIdentityAge: "5m"} ++ c := jwt.MapClaims{"iss": a.Issuer, "aud": a.Audience, "sub": "user:synthetic", "iat": now.Add(-time.Second).Unix(), "exp": now.Add(time.Minute).Unix(), "tenant": "tenant:synthetic", "principal_type": "human", "tenant_source": "directory", "assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []string{"pwd", "otp"}, "at": now.Add(-time.Second).Unix()}} ++ return a, key, c, now ++} ++func signedIdentity(t *testing.T, key ed25519.PrivateKey, c jwt.MapClaims, mutate func(*jwt.Token)) string { ++ t.Helper() ++ token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ if mutate != nil { ++ mutate(token) ++ } ++ raw, e := token.SignedString(key) ++ if e != nil { ++ t.Fatal(e) ++ } ++ return raw ++} ++func TestVerifiedApplicationIdentityActualJOSE(t *testing.T) { ++ a, key, c, now := identityFixture(t) ++ raw := signedIdentity(t, key, c, nil) ++ got, e := VerifyApplicationIdentity(raw, a, now) ++ if e != nil { ++ t.Fatal(e) ++ } ++ if got.Subject != "user:synthetic" || got.Tenant != "tenant:synthetic" || got.Assurance != "aal2" || got.Issuer != a.Issuer || string(got.PrincipalType) != "human" { ++ t.Fatal("identity did not come from verified token") ++ } ++ cases := map[string]func(jwt.MapClaims){"issuer": func(c jwt.MapClaims) { c["iss"] = "https://other.invalid" }, "audience": func(c jwt.MapClaims) { c["aud"] = "other" }, "expired": func(c jwt.MapClaims) { c["exp"] = now.Add(-time.Nanosecond).Unix() }, "future-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(time.Minute).Unix() }, "old-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(-10 * time.Minute).Unix() }, "no-exp": func(c jwt.MapClaims) { delete(c, "exp") }, "no-iat": func(c jwt.MapClaims) { delete(c, "iat") }, "no-tenant": func(c jwt.MapClaims) { delete(c, "tenant") }, "no-principal": func(c jwt.MapClaims) { delete(c, "principal_type") }, "no-assurance": func(c jwt.MapClaims) { delete(c, "assurance") }, "multi-audience-no-azp": func(c jwt.MapClaims) { c["aud"] = []string{"forgejo", "other"} }, "empty-subject": func(c jwt.MapClaims) { c["sub"] = "" }} ++ for name, change := range cases { ++ t.Run(name, func(t *testing.T) { ++ copy := jwt.MapClaims{} ++ for k, v := range c { ++ copy[k] = v ++ } ++ change(copy) ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, copy, nil), a, now); e == nil { ++ t.Fatal("unverified identity accepted") ++ } ++ }) ++ } ++ t.Run("wrong-key", func(t *testing.T) { ++ _, other, _ := ed25519.GenerateKey(rand.Reader) ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, other, c, nil), a, now); e == nil { ++ t.Fatal("wrong key accepted") ++ } ++ }) ++ t.Run("unknown-key", func(t *testing.T) { ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["kid"] = "other" }), a, now); e == nil { ++ t.Fatal("unknown key accepted") ++ } ++ }) ++ t.Run("wrong-type", func(t *testing.T) { ++ if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["typ"] = "at+jwt" }), a, now); e == nil { ++ t.Fatal("access token accepted as ID token") ++ } ++ }) ++ t.Run("algorithm-confusion", func(t *testing.T) { ++ token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ raw, _ := token.SignedString([]byte(a.IdentityKeys["synthetic"])) ++ if _, e := VerifyApplicationIdentity(raw, a, now); e == nil { ++ t.Fatal("algorithm confusion accepted") ++ } ++ }) ++ t.Run("none", func(t *testing.T) { ++ token := jwt.NewWithClaims(jwt.SigningMethodNone, c) ++ token.Header["kid"] = "synthetic" ++ token.Header["typ"] = "JWT" ++ raw, _ := token.SignedString(jwt.UnsafeAllowNoneSignatureType) ++ if _, e := VerifyApplicationIdentity(raw, a, now); e == nil { ++ t.Fatal("unsigned identity accepted") ++ } ++ }) ++} +--- a/services/auth/source/oauth2/providers_openid.go ++++ b/services/auth/source/oauth2/providers_openid.go +@@ -4,6 +4,7 @@ + package oauth2 + + import ( ++ "errors" + "html/template" + + "forgejo.org/modules/log" +@@ -43,6 +44,22 @@ + if err != nil { + log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err) + } ++ if err != nil { ++ return nil, err ++ } ++ if source.RequiredClaimValues != nil { ++ if err := ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues); err != nil { ++ return nil, err ++ } ++ if provider.OpenIDConfig == nil || provider.SkipUserInfoRequest { ++ return nil, errors.New("required-claim-values needs current UserInfo") ++ } ++ guarded, err := RequireCurrentUserInfoClaim(provider.Client(), provider.OpenIDConfig.UserInfoEndpoint, source.RequiredClaimName) ++ if err != nil { ++ return nil, err ++ } ++ provider.HTTPClient = guarded ++ } + return provider, err + } + +--- /dev/null ++++ b/services/auth/source/oauth2/required_claim_values.go +@@ -0,0 +1,202 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++ ++package oauth2 ++ ++import ( ++ "bytes" ++ "encoding/json" ++ "errors" ++ "io" ++ "net/http" ++ "net/url" ++ "strings" ++ "unicode" ++ "unicode/utf8" ++) ++ ++const maxClaimValues = 1024 ++const maxUserInfoBytes = 1 << 20 ++ ++// ValidateRequiredClaimValues validates only the opt-in list extension. A nil ++// list retains every legacy single-value behavior. An explicitly empty list ++// fails closed rather than turning a configured gate into claim-presence-only. ++func ValidateRequiredClaimValues(name, legacy string, values []string) error { ++ if values == nil { ++ return nil ++ } ++ if !validClaimLiteral(name) || legacy != "" || len(values) == 0 || len(values) > 64 { ++ return errors.New("invalid or conflicting required-claim-values configuration") ++ } ++ seen := map[string]bool{} ++ for _, value := range values { ++ if !validClaimLiteral(value) || seen[value] { ++ return errors.New("invalid required-claim-values literal") ++ } ++ seen[value] = true ++ } ++ return nil ++} ++ ++func validClaimLiteral(value string) bool { ++ return value != "" && len(value) <= 256 && utf8.ValidString(value) && ++ value == strings.TrimSpace(value) && !strings.ContainsAny(value, "\x00\r\n,") && ++ !strings.ContainsFunc(value, func(r rune) bool { return unicode.IsControl(r) || r == utf8.RuneError }) ++} ++ ++// strictClaimValues accepts only a well-formed claim array. No scalar parsing, ++// coercion, regex or prefix matching is introduced into the new gate. ++func strictClaimValues(raw any) ([]string, bool) { ++ var values []string ++ switch v := raw.(type) { ++ case []string: ++ values = v ++ case []any: ++ values = make([]string, len(v)) ++ for i, value := range v { ++ s, ok := value.(string) ++ if !ok { ++ return nil, false ++ } ++ values[i] = s ++ } ++ default: ++ return nil, false ++ } ++ if len(values) > maxClaimValues { ++ return nil, false ++ } ++ for _, value := range values { ++ if !validClaimLiteral(value) { ++ return nil, false ++ } ++ } ++ return values, true ++} ++ ++// MatchRequiredClaimValues evaluates the new consumer gate against current ++// authenticated provider data; it does not mutate or manufacture issuer claims. ++func MatchRequiredClaimValues(claims map[string]any, name, legacy string, required []string) bool { ++ if required == nil || ValidateRequiredClaimValues(name, legacy, required) != nil { ++ return false ++ } ++ raw, present := claims[name] ++ if !present { ++ return false ++ } ++ values, ok := strictClaimValues(raw) ++ if !ok { ++ return false ++ } ++ for _, value := range values { ++ for _, wanted := range required { ++ if value == wanted { ++ return true ++ } ++ } ++ } ++ return false ++} ++ ++// RequireCurrentUserInfoClaim preserves the existing HTTP client settings and ++// adds a strict response guard for this opt-in source only. A current UserInfo ++// array must overwrite any stale ID-token claim. Missing/invalid data and HTTP ++// failures cannot fall back to the ID-token map in Goth's merging code. ++func RequireCurrentUserInfoClaim(client *http.Client, endpoint, claim string) (*http.Client, error) { ++ u, err := url.Parse(endpoint) ++ if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Fragment != "" || !validClaimLiteral(claim) { ++ return nil, errors.New("required-claim-values needs an HTTPS UserInfo endpoint") ++ } ++ if client == nil { ++ client = http.DefaultClient ++ } ++ result := *client ++ base := result.Transport ++ if base == nil { ++ base = http.DefaultTransport ++ } ++ result.Transport = ¤tClaimTransport{base: base, endpoint: endpoint, claim: claim} ++ // Redirects could evade endpoint matching or send a bearer to another origin. ++ // They are unsupported for the new guarded provider; legacy clients are intact. ++ result.CheckRedirect = func(*http.Request, []*http.Request) error { ++ return errors.New("guarded OAuth redirects are not supported") ++ } ++ return &result, nil ++} ++ ++type currentClaimTransport struct { ++ base http.RoundTripper ++ endpoint, claim string ++} ++ ++func (t *currentClaimTransport) RoundTrip(req *http.Request) (*http.Response, error) { ++ response, err := t.base.RoundTrip(req) ++ if req.URL.String() != t.endpoint { ++ return response, err ++ } ++ if err != nil { ++ if response != nil && response.Body != nil { ++ response.Body.Close() ++ } ++ return nil, errors.New("current UserInfo lookup failed") ++ } ++ if response == nil || response.Body == nil { ++ return nil, errors.New("current UserInfo response missing") ++ } ++ defer response.Body.Close() ++ if req.Method != http.MethodGet || response.StatusCode != http.StatusOK { ++ return nil, errors.New("current UserInfo refused") ++ } ++ body, err := io.ReadAll(io.LimitReader(response.Body, maxUserInfoBytes+1)) ++ if err != nil || len(body) > maxUserInfoBytes || !utf8.Valid(body) { ++ return nil, errors.New("current UserInfo response invalid") ++ } ++ // Reject duplicate top-level keys before Unmarshal can hide a conflicting ++ // groups or subject claim. json.RawMessage permits unrelated normal claims. ++ decoder := json.NewDecoder(bytes.NewReader(body)) ++ token, err := decoder.Token() ++ if err != nil || token != json.Delim('{') { ++ return nil, errors.New("current UserInfo object required") ++ } ++ claims := map[string]json.RawMessage{} ++ for decoder.More() { ++ keyToken, err := decoder.Token() ++ if err != nil { ++ return nil, errors.New("current UserInfo key invalid") ++ } ++ key, ok := keyToken.(string) ++ if !ok { ++ return nil, errors.New("current UserInfo key invalid") ++ } ++ if _, duplicate := claims[key]; duplicate { ++ return nil, errors.New("current UserInfo duplicate claim") ++ } ++ var value json.RawMessage ++ if decoder.Decode(&value) != nil { ++ return nil, errors.New("current UserInfo value invalid") ++ } ++ claims[key] = value ++ } ++ if token, err = decoder.Token(); err != nil || token != json.Delim('}') { ++ return nil, errors.New("current UserInfo object invalid") ++ } ++ var trailing any ++ if decoder.Decode(&trailing) != io.EOF { ++ return nil, errors.New("current UserInfo trailing data") ++ } ++ raw, present := claims[t.claim] ++ if !present { ++ return nil, errors.New("current UserInfo required claim missing") ++ } ++ var value any ++ if json.Unmarshal(raw, &value) != nil { ++ return nil, errors.New("current UserInfo required claim invalid") ++ } ++ if _, ok := strictClaimValues(value); !ok { ++ return nil, errors.New("current UserInfo required claim array invalid") ++ } ++ // Return the original public response bytes to Goth. Subject equality and ++ // audience/issuer checks remain in its existing authenticated provider path. ++ response.Body = io.NopCloser(bytes.NewReader(body)) ++ return response, nil ++} +--- /dev/null ++++ b/services/auth/source/oauth2/required_claim_values_test.go +@@ -0,0 +1,144 @@ ++// Copyright 2026 The Forgejo Authors. All rights reserved. ++// SPDX-License-Identifier: MIT ++package oauth2 ++ ++import ( ++ "errors" ++ "io" ++ "net/http" ++ "reflect" ++ "strings" ++ "testing" ++) ++ ++var caseValues = []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} ++ ++type roundTripFunc func(*http.Request) (*http.Response, error) ++ ++func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } ++ ++func mockUserInfo(body string, status int) *http.Client { ++ return &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { ++ return &http.Response{StatusCode: status, Header: http.Header{}, Body: io.NopCloser(strings.NewReader(body)), Request: r}, nil ++ })} ++} ++ ++func TestRequiredClaimExactAnyOfAndNoMutation(t *testing.T) { ++ for _, group := range caseValues { ++ groups := []any{group, "unrelated"} ++ claims := map[string]any{"groups": groups, "other": "preserved"} ++ if !MatchRequiredClaimValues(claims, "groups", "", caseValues) { ++ t.Fatal("individual allowed group rejected") ++ } ++ if !reflect.DeepEqual(claims, map[string]any{"groups": groups, "other": "preserved"}) { ++ t.Fatal("claims mutated") ++ } ++ } ++ if !MatchRequiredClaimValues(map[string]any{"groups": append([]string(nil), caseValues...)}, "groups", "", caseValues) { ++ t.Fatal("multiple allowed groups rejected") ++ } ++ for _, raw := range []any{nil, true, 1, "forge-admins", "forge-admins,forge-coulomb-owners", []any{"forge-admins", 1}, []any{"forge-admins", nil}, []string{}, []any{}, []string{"forge-admins-lookalike"}, []string{" Forge-admins"}, []string{"forge-admins\n"}, []string{"Forge-admins"}, []string{"forge-admins,other"}, []string{"forge-admins", ""}} { ++ if MatchRequiredClaimValues(map[string]any{"groups": raw}, "groups", "", caseValues) { ++ t.Fatal("malformed/nonmatching claim accepted") ++ } ++ } ++ if MatchRequiredClaimValues(map[string]any{}, "groups", "", caseValues) { ++ t.Fatal("absent claim accepted") ++ } ++ if MatchRequiredClaimValues(map[string]any{"unrelated": []string{"forge-admins"}}, "groups", "", caseValues) { ++ t.Fatal("unrelated claim used") ++ } ++} ++ ++func TestRequiredClaimConfigurationRefusalsAndLegacyIsolation(t *testing.T) { ++ if ValidateRequiredClaimValues("", "old arbitrary literal", nil) != nil { ++ t.Fatal("legacy config changed") ++ } ++ for _, cfg := range []struct { ++ name, legacy string ++ values []string ++ }{ ++ {"groups", "", []string{}}, {"groups", "", []string{""}}, {"groups", "old", caseValues}, {"", "", caseValues}, ++ {"groups", "", []string{"a", "a"}}, {"groups", "", []string{"a,b"}}, {"groups", "", []string{" a"}}, ++ } { ++ if ValidateRequiredClaimValues(cfg.name, cfg.legacy, cfg.values) == nil { ++ t.Fatal("invalid config accepted") ++ } ++ } ++ if ValidateRequiredClaimValues("groups", "", caseValues) != nil { ++ t.Fatal("case config invalid") ++ } ++} ++ ++func TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable(t *testing.T) { ++ for _, body := range []string{ ++ `{}`, `{"sub":"alice"}`, `{"groups":null}`, `{"groups":true}`, `{"groups":"forge-admins"}`, ++ `{"groups":["forge-admins",null]}`, `{"groups":["forge-admins",1]}`, ++ `{"groups":["forge-admins"],"groups":[]}`, `{"groups":[]} {}`, `[]`, `{"groups":[]`, ++ `{"groups":["forge-admins",""]}`, strings.Repeat(" ", maxUserInfoBytes+1), ++ } { ++ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := client.Get("https://issuer.example/userinfo") ++ if err == nil { ++ response.Body.Close() ++ t.Fatal("invalid current claim did not fail closed") ++ } ++ } ++ client, _ := RequireCurrentUserInfoClaim(mockUserInfo(`{"groups":[]}`, 503), "https://issuer.example/userinfo", "groups") ++ if response, err := client.Get("https://issuer.example/userinfo"); err == nil { ++ response.Body.Close() ++ t.Fatal("non200 accepted") ++ } ++ broken := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { return nil, errors.New("private backend error") })} ++ client, _ = RequireCurrentUserInfoClaim(broken, "https://issuer.example/userinfo", "groups") ++ if _, err := client.Get("https://issuer.example/userinfo"); err == nil || strings.Contains(err.Error(), "private backend") { ++ t.Fatal("lookup failure not contained") ++ } ++} ++ ++func TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval(t *testing.T) { ++ for _, body := range []string{`{"sub":"alice","groups":[]}`, `{"sub":"alice","groups":["forge-coulomb-readers"]}`} { ++ client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := client.Get("https://issuer.example/userinfo") ++ if err != nil { ++ t.Fatal(err) ++ } ++ b, _ := io.ReadAll(response.Body) ++ response.Body.Close() ++ if string(b) != body { ++ t.Fatal("response changed") ++ } ++ } ++} ++ ++func TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse(t *testing.T) { ++ original := mockUserInfo(`{"unrelated":true}`, 200) ++ guarded, err := RequireCurrentUserInfoClaim(original, "https://issuer.example/userinfo", "groups") ++ if err != nil { ++ t.Fatal(err) ++ } ++ response, err := original.Get("https://issuer.example/userinfo") ++ if err != nil { ++ t.Fatal("original client mutated") ++ } ++ response.Body.Close() ++ response, err = guarded.Get("https://issuer.example/unrelated") ++ if err != nil { ++ t.Fatal("unrelated endpoint changed") ++ } ++ response.Body.Close() ++ if guarded.CheckRedirect == nil || guarded.CheckRedirect(nil, nil) == nil { ++ t.Fatal("redirect can bypass guard") ++ } ++ for _, endpoint := range []string{"", "http://issuer.example/userinfo", "https://user:pass@issuer.example/userinfo", "https://issuer.example/userinfo#fragment"} { ++ if _, err := RequireCurrentUserInfoClaim(original, endpoint, "groups"); err == nil { ++ t.Fatal("invalid endpoint accepted") ++ } ++ } ++} +--- a/services/auth/source/oauth2/source.go ++++ b/services/auth/source/oauth2/source.go +@@ -22,6 +22,7 @@ + Scopes []string + AttributeSSHPublicKey string + RequiredClaimName string ++ RequiredClaimValues []string `json:",omitempty"` + RequiredClaimValue string + GroupClaimName string + AdminGroup string +--- a/services/auth/source/oauth2/source_callout.go ++++ b/services/auth/source/oauth2/source_callout.go +@@ -64,5 +64,11 @@ + return user, err + } + ++ if source.RequiredClaimValues != nil { ++ if err := source.authorizeAccessEngine(request.Context(), user); err != nil { ++ return goth.User{}, err ++ } ++ } ++ + return user, nil + } +--- /dev/null ++++ b/third_party/access-engine-pep/LICENSE +@@ -0,0 +1,151 @@ ++# Target Revenue Source License ++ ++**Version 1.0, Candidate 1 (V1C1)** ++ ++--- ++ ++> **PRELIMINARY CANDIDATE — SUBJECT TO CHANGE — NOT FINAL** ++> ++> This repository is governed by the Target Revenue Source License ++> (TRSL), Version 1, Candidate 1. This is the framework's first working ++> candidate, adopted as the org's preliminary operating license across ++> all repos (maintainer decision, 2026-07-29) during the build/alpha ++> stage — see `SCOPE.md` §1 and Appendix A of the canonical text (link ++> below) for the alpha/beta risk-acceptance decision this reflects and ++> what it does and does not mean. Full specialist legal review is ++> explicitly deferred until the framework moves out of beta. ++> ++> **Canonical source, full candidate-status banner, and Appendix A ++> (non-normative candidate notes tracking every open item):** the ++> `coulomb` org's `target-revenue` repository, ++> `specs/TargetRevenueSourceLicense-V1C1.md` — this file is the operative ++> legal text (Preamble through Section 11) only; the canonical document ++> is authoritative if this copy and that document ever diverge. ++ ++--- ++ ++## Preamble ++ ++This Target Revenue Source License ("**License**") governs the Software identified in the applicable Phase Manifest. It implements the Target Revenue Framework: a defined development Phase accumulates Development Credit and Remission Credit against an immutable Initial Target until the Milestone Release automatically and irrevocably converts to a declared permissive Future License. ++ ++Commercial beneficiaries fund the creation and early availability of a software improvement; once the declared target is satisfied, the governed release becomes permissively open source. ++ ++## 1. Definitions ++ ++Capitalized terms used in this License have the meanings given below. Where a term is also defined in the Phase Manifest or Target Ledger for a specific Phase, the Phase Manifest and Target Ledger govern the *values* (amounts, dates, identifiers) and this License governs the *legal effect* of those values — the two must not be read as conflicting definitions of the same concept. ++ ++**"Commercial Entitlement"** means a right, purchased or otherwise granted under a Commercial Use Agreement, to make Commercial Use of the Software during a Phase. ++ ++**"Commercial Use"** means billing, invoicing, or otherwise charging any customer a fee, subscription, license fee, or other consideration for or in connection with use of the Software, at any time before the Conversion Event for the applicable Phase, regardless of whether the person or organization billed would otherwise qualify for Noncommercial Use. Commercial Use occurs by virtue of such billing alone, whether or not the resulting payment is registered with the Trust Service; in particular, billing a customer for pre-conversion use of the Software without recording the corresponding payment in the applicable Phase's Target Ledger is Commercial Use without a valid Commercial Entitlement — a violation of Section 3, addressed under Section 7 and, where applicable, the Enforcement Network described in the canonical repository's `specs/EnforcementNetworkConcept.md`. ++ ++**"Commercial Use Agreement"** means the separate agreement, referenced by the applicable Phase Manifest, under which a Commercial Entitlement is purchased or granted. This License does not itself set pricing, metering, or payment terms — those are governed by the Commercial Use Agreement. ++ ++**"Conversion Event"** means the moment the Outstanding Target for a Phase reaches zero, as computed from the Phase Manifest and Target Ledger per the Target Ledger Specification. The Conversion Event occurs automatically and is not conditioned on any declaration, attestation, or other act by the Licensor or any Trust Service. ++ ++**"Development Credit"** means the portion of a collected and settled payment explicitly allocated toward satisfying the Initial Target of a specific Phase, as recorded in that Phase's Target Ledger. ++ ++**"Future License"** means the permissive license identified in the applicable Phase Manifest, being either the MIT License or the Apache License, Version 2.0, which applies to the Milestone Release upon the Conversion Event. ++ ++**"Initial Target"** means the immutable monetary target declared for a Phase in its Phase Manifest. ++ ++**"Licensor"** means **Binky Hedgehog GmbH**, the party that publishes the Phase Manifest and holds the rights necessary to grant this License and the Future License for the Milestone Release. ++ ++**"Milestone Release"** means the precisely identified software release designated in the applicable Phase Manifest, identified by an immutable source revision, release artifact, or cryptographic digest. ++ ++**"Noncommercial Use"** means use of the Software for personal purposes, private study, hobby or amateur projects; use by any charitable organization, educational institution, public research organization, or government institution acting in a non-revenue-generating capacity; or other use of a materially similar character. ++ ++**"Outstanding Target"** means, at any time, `max(0, Initial Target − cumulative Development Credit − cumulative Remission Credit)` for a Phase, as computed from that Phase's Target Ledger. ++ ++**"Phase"** means a bounded development undertaking governed by one Initial Target, one Milestone Release, one degeneration policy, and one Future License declaration, as declared in a Phase Manifest. ++ ++**"Phase Manifest"** means the published, immutable declaration identifying a Phase, its Milestone Release, Initial Target, Future License, degeneration policy, and Target Ledger location, as specified in the Phase Manifest Specification. ++ ++**"Remission Credit"** means a transparent, non-revenue reduction of a Phase's Outstanding Target, generated under that Phase's published degeneration policy and recorded in the Target Ledger. ++ ++**"Settled Payment"** means a payment that has cleared through its payment processor and is no longer subject to reversal in the ordinary course (chargeback, dispute, or equivalent), as further specified by the applicable Commercial Use Agreement or monetization extension. ++ ++**"Software"** means the source code, object code, and associated documentation of the Milestone Release identified in the applicable Phase Manifest. ++ ++**"Target Ledger"** means the append-only record of Development Credit, Remission Credit, and correction entries for a Phase, as specified in the Target Ledger Specification. ++ ++**"You"** or **"Licensee"** means the individual or entity exercising rights under this License. ++ ++## 2. Grant of Rights for Noncommercial Use ++ ++Subject to the terms of this License, the Licensor grants You a worldwide, royalty-free, non-exclusive license, during the applicable Phase, to: ++ ++(a) use, reproduce, and study the Software for any Noncommercial Use; ++ ++(b) modify the Software and create derivative works of it for any Noncommercial Use; and ++ ++(c) redistribute the Software and Your modifications, in source or object form, for any Noncommercial Use, provided that You include this License, unmodified, with any such redistribution, and that You do not remove or alter any copyright, patent, trademark, or attribution notices contained in the Software. ++ ++This grant does not extend to Commercial Use. Commercial Use requires a Commercial Entitlement under Section 3. ++ ++## 3. Commercial Use ++ ++You may not make Commercial Use of the Software during the applicable Phase unless You hold a valid, current Commercial Entitlement under a Commercial Use Agreement with the Licensor covering the applicable Phase. A Commercial Entitlement granted under one Phase's Commercial Use Agreement does not extend to a later Phase's Milestone Release unless the Commercial Use Agreement expressly says so. ++ ++This Section 3 states the existence and boundary of the commercial-use restriction. It does not itself set pricing, invoicing, metering, audit rights, or payment terms — those are governed exclusively by the applicable Commercial Use Agreement. ++ ++## 4. Patent License ++ ++Subject to the terms of this License, each contributor to the Software grants You, during the applicable Phase and solely to the extent of rights granted under Sections 2 and 3, a perpetual (subject to the termination below), worldwide, non-exclusive, no-charge, royalty-free patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Software, limited to those patent claims licensable by that contributor that are necessarily infringed by their contribution(s) alone or by combination of their contribution(s) with the Software. ++ ++If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software or a contribution incorporated within it constitutes direct or contributory patent infringement, then any patent licenses granted to You under this Section 4 for the Software shall terminate as of the date such litigation is filed. ++ ++## 5. Automatic Conversion to the Future License ++ ++**5.1 Automatic effect.** Upon the Conversion Event for a Phase, the rights and restrictions in Sections 3 (Commercial Use) of this License, as they apply to that Phase's Milestone Release, terminate automatically. In their place, the Milestone Release is licensed under the Future License identified in that Phase's Phase Manifest, effective as of the Conversion Event, without any further act, declaration, or attestation required by the Licensor, any Trust Service, or any other party. ++ ++**5.2 Irrevocability.** Once a valid Conversion Event has occurred for a Phase, no subsequent refund, chargeback, accounting correction, dispute, or termination of this License for an unrelated breach shall revoke, suspend, or otherwise impair the Future License grant for that Phase's Milestone Release. Any shortfall or dispute arising after a Conversion Event is a commercial or accounting matter between the relevant parties and does not reinstate a commercial-use restriction over already-converted Software. ++ ++**5.3 Prior freedom preserved.** A later Phase covering subsequent improvements to the Software does not restrict, withdraw, or otherwise affect the rights granted under the Future License for an earlier Phase's already-converted Milestone Release. ++ ++**5.4 Evidence, not cause.** A Trust Service may publish a Conversion Attestation documenting a Conversion Event. Such an attestation is evidence that the Conversion Event occurred; it is not a condition of, and its absence or delay does not postpone, the automatic effect described in Section 5.1. Any person may independently verify whether a Conversion Event has occurred directly from the Phase Manifest and Target Ledger. ++ ++## 6. Successive Phases ++ ++The Licensor may declare a new Phase covering subsequent improvements to the Software following a Milestone Release's Conversion Event. Each Phase is independently governed by its own Phase Manifest, Initial Target, degeneration policy, and Target Ledger. Nothing in a later Phase's Phase Manifest may be construed to reduce or withdraw rights already granted under Section 5 for an earlier Phase's Milestone Release. ++ ++## 7. Term and Termination ++ ++**7.1 Term.** This License applies to the Software for the duration of the applicable Phase, and, for the Milestone Release, indefinitely following that Phase's Conversion Event under the Future License. ++ ++**7.2 Termination for breach.** If You breach Section 3 (Commercial Use) or Section 2(c) (redistribution notice requirement), the Licensor may terminate this License as to You. Before such termination becomes effective, the Licensor shall provide You written notice of the breach; if You cure the breach within thirty (30) days of that notice, this License continues in effect. A second breach of the same provision within twelve (12) months may be terminated immediately without a further cure opportunity. ++ ++**7.3 Effect of termination.** Termination under this Section 7 affects only Your rights under Sections 2 and 3 for the Phase in which the breach occurred. It does not affect any rights already vested under Section 5 (Automatic Conversion) for a Milestone Release whose Conversion Event has already occurred, per Section 5.2. ++ ++**7.4 Public record of breach and resolution.** The Licensor shall cause the Trust Service to publish, as part of the public record for the affected Phase, notice of: (a) any breach notice issued under Section 7.2, stating the general nature of the breach and the date of notice; (b) whether the breach was cured within the applicable cure period, and the date of cure; and (c) any termination determination made under this Section 7, including its effective date and scope. This public record exists to give the ecosystem a transparent, verifiable conformity signal for the Phase, distinct from and in addition to the Development Credit and Remission Credit facts already published under Section 5.4 and the Target Ledger Specification. ++ ++A breach that You dispute, and that has not been finally determined, shall be recorded as **alleged**; it shall be recorded as **determined** only once the cure period has run without cure, or the dispute has been resolved against You under the applicable Commercial Use Agreement's dispute process, if any. The Trust Service shall update the record promptly upon resolution in either direction. Recording an alleged or determined breach under this Section 7.4 is a ministerial act of publishing the Licensor's determination (or a dispute process's outcome); it does not give the Trust Service discretionary authority to decide whether a breach occurred, consistent with Section 5.4's evidence-not-cause principle. ++ ++Whether, and under what conditions, the public record identifies a Commercial Entitlement holder by name is governed exclusively by the applicable Commercial Use Agreement, which the Licensor and that Commercial Entitlement holder negotiate and agree to directly. This License does not itself set a naming default. Where no Commercial Use Agreement addresses the question, or where the affected party has no Commercial Use Agreement at all (for example, a Section 2(c) breach by a Noncommercial Use licensee), the public record states the Phase and breach category only, without naming the party. ++ ++## 8. Disclaimer of Warranty ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE LICENSOR DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR-FREE OR THAT ANY PHASE WILL REACH ITS CONVERSION EVENT. ++ ++## 9. Limitation of Liability ++ ++IN NO EVENT SHALL THE LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EXCEPT TO THE EXTENT SUCH LIMITATION IS PROHIBITED BY APPLICABLE LAW. ++ ++## 10. Trademarks ++ ++This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary attribution. ++ ++## 11. General Provisions ++ ++**11.1 Governing law and venue.** Adopted for alpha/beta 2026-07-29: disputes arising under this License shall be resolved by binding arbitration, seated at a neutral, arbitration-mature venue (Singapore or London are the two candidates concretely supported by current research), rather than by litigation in a national court. The specific arbitral institution and substantive governing law remain a per-deployment blank pending final selection; they are not fixed by this candidate. See the canonical repository's `history/260729-TRSL-Jurisdiction-Synthesis.md` §2. ++ ++**11.2 Severability.** If any provision of this License is held unenforceable, the remaining provisions remain in full force, and the unenforceable provision shall be reformed to the minimum extent necessary to make it enforceable. ++ ++**11.3 No waiver.** Failure to enforce any provision of this License is not a waiver of future enforcement of that or any other provision. ++ ++**11.4 Entire agreement (as to licensing).** This License, together with the applicable Phase Manifest and, where applicable, the Commercial Use Agreement, constitutes the entire agreement between You and the Licensor regarding the Software's licensing terms. Operations, service, and consulting arrangements are governed by separate agreements, if any, and are not part of this License. ++ ++**11.5 Definitions control.** Marketing materials, documentation, or other non-normative communications about the Software must not describe pre-Conversion-Event Software as "Open Source," "free software," or "open core." Pre-conversion Noncommercial Use is **source-available**; pre-conversion Commercial Use requires a **Commercial Entitlement**; only post-conversion Software may be described as Open Source, under the Future License. ++ ++--- ++ ++**No Phase is currently declared for this repository under this License.** Until a Phase Manifest is published and registered with the Trust Service for a Milestone Release in this repository, Sections 2–7 above have no operative subject matter here — this License establishes the governing framework in advance of that declaration, consistent with the org-wide rollout decision recorded in `target-revenue`'s `workplans/TREV-WP-0008-governance-and-pilot-rollout.md`. +--- /dev/null ++++ b/third_party/access-engine-pep/go.mod +@@ -0,0 +1,3 @@ ++module github.com/netkingdom/access-engine ++ ++go 1.24 +--- /dev/null ++++ b/third_party/access-engine-pep/internal/sign/sign.go +@@ -0,0 +1,95 @@ ++// Package sign attaches a detached Ed25519 signature to a decision envelope ++// (FLEX-WP-0024). The signed material is json.Marshal of the envelope with ++// the signature field omitted. ++package sign ++ ++import ( ++ "bytes" ++ "crypto/ed25519" ++ "encoding/base64" ++ "encoding/json" ++ "fmt" ++ "io" ++ ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++const AlgorithmEd25519 = "ed25519" ++ ++// CanonicalBytes is the exact payload that is signed and verified: the ++// envelope as emitted, with signature absent. ++func CanonicalBytes(envelope api.DecisionEnvelope) ([]byte, error) { ++ envelope.Signature = nil ++ data, err := json.Marshal(envelope) ++ if err != nil { ++ return nil, fmt.Errorf("marshal canonical envelope: %w", err) ++ } ++ return data, nil ++} ++ ++func Sign(envelope *api.DecisionEnvelope, private ed25519.PrivateKey, kid string) error { ++ if envelope == nil { ++ return fmt.Errorf("envelope is required") ++ } ++ if len(private) != ed25519.PrivateKeySize { ++ return fmt.Errorf("ed25519 private key has length %d", len(private)) ++ } ++ if kid == "" { ++ return fmt.Errorf("kid is required") ++ } ++ payload, err := CanonicalBytes(*envelope) ++ if err != nil { ++ return err ++ } ++ sig := ed25519.Sign(private, payload) ++ envelope.Signature = &api.EnvelopeSignature{ ++ Mode: api.EnvelopeSignatureSigned, ++ Alg: AlgorithmEd25519, ++ Kid: kid, ++ Value: base64.RawURLEncoding.EncodeToString(sig), ++ } ++ return nil ++} ++ ++func Verify(envelope api.DecisionEnvelope, keys map[string]ed25519.PublicKey) error { ++ sig := envelope.Signature ++ if sig == nil || sig.Mode != api.EnvelopeSignatureSigned { ++ return fmt.Errorf("envelope is not signed") ++ } ++ if sig.Alg != AlgorithmEd25519 { ++ return fmt.Errorf("unsupported signature alg %q", sig.Alg) ++ } ++ pub, ok := keys[sig.Kid] ++ if !ok { ++ return fmt.Errorf("unknown signature kid %q", sig.Kid) ++ } ++ raw, err := base64.RawURLEncoding.DecodeString(sig.Value) ++ if err != nil { ++ return fmt.Errorf("decode signature: %w", err) ++ } ++ payload, err := CanonicalBytes(envelope) ++ if err != nil { ++ return err ++ } ++ if !ed25519.Verify(pub, payload, raw) { ++ return fmt.Errorf("signature does not match envelope") ++ } ++ return nil ++} ++ ++// DecodeEnvelope preserves JSON numeric kinds/lexemes emitted by task inputs. ++// Legacy producer envelopes remain byte-compatible. It does not invent another ++// signature canonicalization: CanonicalBytes still calls typed json.Marshal. ++func DecodeEnvelope(raw []byte) (api.DecisionEnvelope, error) { ++ var envelope api.DecisionEnvelope ++ decoder := json.NewDecoder(bytes.NewReader(raw)) ++ decoder.UseNumber() ++ decoder.DisallowUnknownFields() ++ if err := decoder.Decode(&envelope); err != nil { ++ return envelope, err ++ } ++ if _, err := decoder.Token(); err != io.EOF { ++ return api.DecisionEnvelope{}, fmt.Errorf("trailing envelope data") ++ } ++ return envelope, nil ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/canonical.go +@@ -0,0 +1,699 @@ ++package api ++ ++import ( ++ "crypto/sha256" ++ "encoding/hex" ++ "encoding/json" ++ "fmt" ++ "strings" ++ "time" ++) ++ ++// ProtectedSystemManifest describes a system that delegates authorization to ++// flex-auth. ++type ProtectedSystemManifest struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Description string `json:"description,omitempty" yaml:"description,omitempty"` ++ ClientBinding *ClientBinding `json:"client_binding,omitempty" yaml:"client_binding,omitempty"` ++ ResourceTypes []ResourceType `json:"resource_types,omitempty" yaml:"resource_types,omitempty"` ++ Actions []ActionDefinition `json:"actions,omitempty" yaml:"actions,omitempty"` ++ CaringProfiles []string `json:"caring_profiles,omitempty" yaml:"caring_profiles,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// ClientBinding names the key-cape client a protected system's users sign in ++// through. It is descriptive only: the readback registry projection lists it, ++// and it never decides an allow (FLEX-DEC-2026-019). ++type ClientBinding struct { ++ Issuer string `json:"issuer" yaml:"issuer"` ++ ClientID string `json:"client_id" yaml:"client_id"` ++} ++ ++// ResourceType describes a resource namespace entry owned by a protected system. ++type ResourceType struct { ++ Name string `json:"name" yaml:"name"` ++ ParentTypes []string `json:"parent_types,omitempty" yaml:"parent_types,omitempty"` ++ ScopeLevel ScopeLevel `json:"scope_level,omitempty" yaml:"scope_level,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// ActionDefinition maps a protected-system action to CARING capabilities. ++type ActionDefinition struct { ++ Name string `json:"name" yaml:"name"` ++ Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// SubjectManifest declares subjects, groups, teams, and tenants for local ++// registry loading. ++type SubjectManifest struct { ++ ID string `json:"id" yaml:"id"` ++ Subjects []Subject `json:"subjects,omitempty" yaml:"subjects,omitempty"` ++ Groups []Group `json:"groups,omitempty" yaml:"groups,omitempty"` ++ Teams []Team `json:"teams,omitempty" yaml:"teams,omitempty"` ++ Tenants []Tenant `json:"tenants,omitempty" yaml:"tenants,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Subject is a human, service, automation, agent, or other acting identity. ++type Subject struct { ++ ID string `json:"id" yaml:"id"` ++ Type SubjectType `json:"type" yaml:"type"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ OrganizationRelation OrganizationRelation `json:"organization_relation,omitempty" yaml:"organization_relation,omitempty"` ++ Roles []CanonicalRole `json:"roles,omitempty" yaml:"roles,omitempty"` ++ Groups []string `json:"groups,omitempty" yaml:"groups,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Claims map[string]any `json:"claims,omitempty" yaml:"claims,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Group is an assignment convenience, not a canonical role. ++type Group struct { ++ ID string `json:"id" yaml:"id"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ Members []string `json:"members,omitempty" yaml:"members,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Team is a group-like ownership unit used by protected systems. ++type Team struct { ++ ID string `json:"id" yaml:"id"` ++ DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"` ++ Members []string `json:"members,omitempty" yaml:"members,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Tenant is a structural isolation boundary. ++type Tenant struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// RelationshipFact records a relation between subjects, groups, teams, tenants, ++// and resources. ++type RelationshipFact struct { ++ ID string `json:"id" yaml:"id"` ++ System string `json:"system,omitempty" yaml:"system,omitempty"` ++ Subject string `json:"subject" yaml:"subject"` ++ Relation string `json:"relation" yaml:"relation"` ++ Object string `json:"object" yaml:"object"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"` ++ Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// PolicyPackageMetadata is the frontmatter contract for Rego-in-Markdown ++// policy packages. ++type PolicyPackageMetadata struct { ++ ID string `json:"id" yaml:"id"` ++ Name string `json:"name,omitempty" yaml:"name,omitempty"` ++ Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` ++ Version string `json:"version" yaml:"version"` ++ Status string `json:"status,omitempty" yaml:"status,omitempty"` ++ Package string `json:"package" yaml:"package"` ++ Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"` ++ Owner string `json:"owner,omitempty" yaml:"owner,omitempty"` ++ Fixtures []string `json:"fixtures,omitempty" yaml:"fixtures,omitempty"` ++ Caring CaringPolicyMetadata `json:"caring" yaml:"caring"` ++ Activation map[string]any `json:"activation,omitempty" yaml:"activation,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++ // AllowTTL is a Go duration (for example "15m") that bounds every allow ++ // this package produces. Omit to use DefaultAllowTTL. "none" or "0s" ++ // means no stated end; the engine denies those allows (§9.7.1). ++ AllowTTL string `json:"allow_ttl,omitempty" yaml:"allow_ttl,omitempty"` ++} ++ ++// CaringPolicyMetadata declares the CARING envelope a policy governs. ++type CaringPolicyMetadata struct { ++ Profile string `json:"profile" yaml:"profile"` ++ Enforce bool `json:"enforce,omitempty" yaml:"enforce,omitempty"` ++ CanonicalRoles []CanonicalRole `json:"canonical_roles,omitempty" yaml:"canonical_roles,omitempty"` ++ OrganizationRelations []OrganizationRelation `json:"organization_relations,omitempty" yaml:"organization_relations,omitempty"` ++ Scopes []CaringScope `json:"scopes,omitempty" yaml:"scopes,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// PolicyFixture binds a check request to an expected decision. ++type PolicyFixture struct { ++ ID string `json:"id" yaml:"id"` ++ Request CheckRequest `json:"request" yaml:"request"` ++ Expect DecisionExpectation `json:"expect" yaml:"expect"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// DecisionExpectation is the compact fixture expectation for policy tests. ++type DecisionExpectation struct { ++ Effect DecisionEffect `json:"effect" yaml:"effect"` ++ Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` ++ Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++} ++ ++// CheckRequest is the stable protected-system-facing decision request. ++type CheckRequest struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ CaringContext *CaringAccessDescriptor `json:"caring_context,omitempty" yaml:"caring_context,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++ ++ // Purpose is empty on every enforcement request. The readback pin sets it ++ // to PurposeReadback, and it is hashed into every request digest, so an ++ // advisory readback decision can never pass a PEP's §6.4.2 replay test for ++ // an enforcement request (FLEX-DEC-2026-019). /v1/check and /v1/batch_check ++ // refuse a request that carries it. ++ Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"` ++} ++ ++// PurposeReadback marks an advisory application-access readback decision. ++const PurposeReadback = "readback" ++ ++// BatchCheckRequest evaluates one subject/action against multiple resources. ++type BatchCheckRequest struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resources []ResourceRef `json:"resources" yaml:"resources"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++} ++ ++// SubjectRef is a normalized subject reference in request and decision shapes. ++type SubjectRef struct { ++ ID string `json:"id" yaml:"id"` ++ Type SubjectType `json:"type,omitempty" yaml:"type,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// ResourceRef is a normalized resource reference in request and decision shapes. ++type ResourceRef struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type,omitempty" yaml:"type,omitempty"` ++ System string `json:"system,omitempty" yaml:"system,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// DecisionEffect is the stable decision outcome vocabulary. ++type DecisionEffect string ++ ++const ( ++ DecisionEffectAllow DecisionEffect = "allow" ++ DecisionEffectDeny DecisionEffect = "deny" ++ DecisionEffectRedact DecisionEffect = "redact" ++ DecisionEffectAuditOnly DecisionEffect = "audit_only" ++ DecisionEffectNotApplicable DecisionEffect = "not_applicable" ++) ++ ++// DecisionEffects returns the complete effect vocabulary. Every effect is an ++// emission class in cadence.yaml; a test fails if a constant above is missing ++// here, so a new effect cannot ship unclassified (FLEX-WP-0031-T03). ++func DecisionEffects() []DecisionEffect { ++ return []DecisionEffect{ ++ DecisionEffectAllow, ++ DecisionEffectDeny, ++ DecisionEffectRedact, ++ DecisionEffectAuditOnly, ++ DecisionEffectNotApplicable, ++ } ++} ++ ++// Restricts reports whether an effect withholds authority. This classification ++// does not exempt an effect from required durable emission (GH-DEC-2026-024). ++func (e DecisionEffect) Restricts() bool { ++ switch e { ++ case DecisionEffectDeny, DecisionEffectRedact, DecisionEffectNotApplicable: ++ return true ++ } ++ return false ++} ++ ++// DecisionRecordContractV1 is the published decision-record contract identifier. ++const DecisionRecordContractV1 = "flex-auth.decision-record.v1" ++ ++// DecisionEnvelope is the stable response produced by standalone and delegated ++// evaluators. It is flex-auth's published decision-record contract (§17). ++type DecisionEnvelope struct { ++ ID string `json:"id" yaml:"id"` ++ ContractVersion string `json:"contract_version,omitempty" yaml:"contract_version,omitempty"` ++ RequestID string `json:"request_id,omitempty" yaml:"request_id,omitempty"` ++ Effect DecisionEffect `json:"effect" yaml:"effect"` ++ Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` ++ MatchedPolicyVersion string `json:"matched_policy_version,omitempty" yaml:"matched_policy_version,omitempty"` ++ MatchedRule string `json:"matched_rule,omitempty" yaml:"matched_rule,omitempty"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Binding *DecisionBinding `json:"binding,omitempty" yaml:"binding,omitempty"` ++ Lifetime *DecisionLifetime `json:"lifetime,omitempty" yaml:"lifetime,omitempty"` ++ Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"` ++ Diagnostics map[string]any `json:"diagnostics,omitempty" yaml:"diagnostics,omitempty"` ++ Provenance DecisionProvenance `json:"provenance" yaml:"provenance"` ++ Signature *EnvelopeSignature `json:"signature,omitempty" yaml:"signature,omitempty"` ++ Caring *CaringDecisionMetadata `json:"caring,omitempty" yaml:"caring,omitempty"` ++} ++ ++// EnvelopeSignatureMode is how the responder authenticates the envelope. ++type EnvelopeSignatureMode string ++ ++const ( ++ EnvelopeSignatureSigned EnvelopeSignatureMode = "signed" ++ EnvelopeSignatureUnsigned EnvelopeSignatureMode = "unsigned" ++) ++ ++// EnvelopeSignature is a detached signature over the canonical envelope with ++// this field omitted (FLEX-WP-0024). It is not decision material and is not ++// part of request_digest. ++type EnvelopeSignature struct { ++ Mode EnvelopeSignatureMode `json:"mode" yaml:"mode"` ++ Alg string `json:"alg,omitempty" yaml:"alg,omitempty"` ++ Kid string `json:"kid,omitempty" yaml:"kid,omitempty"` ++ Value string `json:"value,omitempty" yaml:"value,omitempty"` ++} ++ ++func UnsignedEnvelopeSignature() *EnvelopeSignature { ++ return &EnvelopeSignature{Mode: EnvelopeSignatureUnsigned} ++} ++ ++// DecisionLifetimeKind identifies how an allow ends. ++type DecisionLifetimeKind string ++ ++const ( ++ DecisionLifetimeTTL DecisionLifetimeKind = "ttl" ++) ++ ++// DefaultAllowTTL is the engine default when a policy package omits allow_ttl. ++const DefaultAllowTTL = 15 * time.Minute ++ ++// ReasonAllowLifetimeUnstated is the deny reason for an allow with no stated end. ++const ReasonAllowLifetimeUnstated = "allow_lifetime_unstated" ++ ++// DecisionLifetime bounds an allow (§9.7.1). flex-auth has no session concept, ++// so the first honest shape is a policy-package-declared TTL. ++type DecisionLifetime struct { ++ Kind DecisionLifetimeKind `json:"kind" yaml:"kind"` ++ TTL string `json:"ttl,omitempty" yaml:"ttl,omitempty"` ++ NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"` ++ ExpiresAt string `json:"expires_at" yaml:"expires_at"` ++} ++ ++// DecisionBinding is the exact normalized authorization request evaluated by ++// a decision. It lets a consumer verify structured action, target, actor, and ++// context fields without parsing reason or diagnostic prose. ++type DecisionBinding struct { ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Action string `json:"action" yaml:"action"` ++ Resource ResourceRef `json:"resource" yaml:"resource"` ++ Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"` ++ // Purpose is "readback" on an advisory readback decision and absent on an ++ // enforcement decision (FLEX-DEC-2026-019). ++ Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"` ++ RequestDigest string `json:"request_digest" yaml:"request_digest"` ++ ++ // ApprovalBindingDigest is RequestDigest computed with the approval claim ++ // removed from context. It exists to break a circularity: an approval's ++ // pdp_digest is recorded at issue time, but the request that later carries ++ // the claim inside its hashed context has a different RequestDigest by ++ // construction, so a claim can never name the request that carries it. ++ // ++ // This digest is stable across attaching the claim, so it is the value ++ // claim.binding.pdp_digest must equal (GH-DEC-2026-008). It is NOT a replay ++ // identity and must not be used as one: two requests differing only in ++ // which approval was presented share it, and the decision does not. ++ ApprovalBindingDigest string `json:"approval_binding_digest,omitempty" yaml:"approval_binding_digest,omitempty"` ++ ++ // SubmittedRequestDigest is RequestDigest over the request exactly as the ++ // consumer sent it, before the evaluator overlays registry facts onto the ++ // subject and resource. ++ // ++ // It exists because RequestDigest is not consumer-computable and was ++ // published as though it were. The evaluator hashes the ENRICHED request — ++ // subject.tenant, subject.attributes, and resource attributes the registry ++ // contributed — and the registry is flex-auth's, so a consumer recomputing ++ // the digest over what it sent gets a different value on every real allow. ++ // secrets-engine found this on its first live request (FLEX-DEC-2026-012). ++ // ++ // This is the digest a PEP compares for §6.4 obligation 2's replay test. ++ // Nothing is lost by hashing the pre-enrichment form: enrichment is a ++ // function of the request and the registry snapshot, and ++ // provenance.registry_snapshot_digest already pins the snapshot, so ++ // SubmittedRequestDigest together with that digest identifies the evaluated ++ // request completely. ++ SubmittedRequestDigest string `json:"submitted_request_digest,omitempty" yaml:"submitted_request_digest,omitempty"` ++} ++ ++// ApprovalContextKey is the context key carrying an approval-engine ++// approval-claim. It is excluded from ApprovalBindingDigest and from nothing ++// else. ++const ApprovalContextKey = "approval" ++ ++// requestDigestMaterial is the exact tuple hashed for §6.4.2 replay. Request ++// id, policy version, and caring_context are excluded: id is correlation, the ++// version is provenance, and caring_context is an input-claim digest. ++type requestDigestMaterial struct { ++ Tenant string `json:"tenant,omitempty"` ++ Subject SubjectRef `json:"subject"` ++ Action string `json:"action"` ++ Resource ResourceRef `json:"resource"` ++ Context map[string]any `json:"context,omitempty"` ++ Purpose string `json:"purpose,omitempty"` ++} ++ ++// NewDecisionBinding returns a stable structured binding for the exact request ++// an evaluator consumed. ++// ++// Prefer NewDecisionBindingFor, which also records the digest of the request as ++// submitted. This form leaves SubmittedRequestDigest empty, which reads as "the ++// evaluator did not record one" rather than "the two are equal". ++func NewDecisionBinding(request CheckRequest) *DecisionBinding { ++ contextCopy := make(map[string]any, len(request.Context)) ++ for key, value := range request.Context { ++ contextCopy[key] = value ++ } ++ binding := &DecisionBinding{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: contextCopy, ++ Purpose: request.Purpose, ++ RequestDigest: RequestDigest(request), ++ } ++ if _, carries := request.Context[ApprovalContextKey]; carries { ++ binding.ApprovalBindingDigest = ApprovalBindingDigest(request) ++ } ++ return binding ++} ++ ++// NewDecisionBindingFor records the binding of the evaluated request together ++// with the digest of the request as submitted. ++// ++// The two arguments are the same request before and after the evaluator ++// overlaid registry facts. Where a request named nothing the registry knows they ++// are equal, and the field is emitted anyway: a consumer that only sees it on ++// enriched decisions would build a check that passes by absence. ++func NewDecisionBindingFor(evaluated, submitted CheckRequest) *DecisionBinding { ++ binding := NewDecisionBinding(evaluated) ++ binding.SubmittedRequestDigest = RequestDigest(submitted) ++ return binding ++} ++ ++// ApprovalBindingDigest is RequestDigest over the same material with the ++// approval claim removed from context. ++// ++// When a request carries no approval claim the two are identical, which is the ++// point: an approval issued against a claim-free Check records that Check's ++// request_digest, and the later claim-bearing request reproduces the same value ++// here. Comparing it to claim.binding.pdp_digest establishes correspondence by ++// identity rather than by translating between two action vocabularies ++// (GH-DEC-2026-008). ++func ApprovalBindingDigest(request CheckRequest) string { ++ if _, carries := request.Context[ApprovalContextKey]; !carries { ++ return RequestDigest(request) ++ } ++ stripped := make(map[string]any, len(request.Context)) ++ for key, value := range request.Context { ++ if key == ApprovalContextKey { ++ continue ++ } ++ stripped[key] = value ++ } ++ if len(stripped) == 0 { ++ stripped = nil ++ } ++ return CanonicalDigest(requestDigestMaterial{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: stripped, ++ Purpose: request.Purpose, ++ }) ++} ++ ++// RequestDigest is the mechanical §6.4.2 replay test: SHA-256 over canonical ++// JSON of tenant, subject, action, resource, context, and purpose. Purpose is ++// omitted when empty, so enforcement digests are unchanged by its existence. ++func RequestDigest(request CheckRequest) string { ++ return CanonicalDigest(requestDigestMaterial{ ++ Tenant: request.Tenant, ++ Subject: request.Subject, ++ Action: request.Action, ++ Resource: request.Resource, ++ Context: request.Context, ++ Purpose: request.Purpose, ++ }) ++} ++ ++// CanonicalDigest returns "sha256:" plus the hex SHA-256 of canonical JSON. ++// encoding/json sorts map keys, so two equal Go values agree. ++func CanonicalDigest(value any) string { ++ data, err := json.Marshal(value) ++ if err != nil { ++ sum := sha256.Sum256(nil) ++ return "sha256:" + hex.EncodeToString(sum[:]) ++ } ++ sum := sha256.Sum256(data) ++ return "sha256:" + hex.EncodeToString(sum[:]) ++} ++ ++// InputClaimDigests hashes the request-time claim classes the evaluator joined. ++func InputClaimDigests(request CheckRequest) map[string]string { ++ digests := make(map[string]string) ++ if len(request.Context) > 0 { ++ digests["context"] = CanonicalDigest(request.Context) ++ } ++ if request.CaringContext != nil { ++ digests["caring_context"] = CanonicalDigest(request.CaringContext) ++ } ++ if len(digests) == 0 { ++ return nil ++ } ++ return digests ++} ++ ++// DecisionCompletion carries evaluator-side inputs used to finish an envelope. ++type DecisionCompletion struct { ++ AllowTTL string ++ Now time.Time ++} ++ ++// CompleteDecision stamps contract version, input-claim digests, decision time, ++// and an explicit allow lifetime. An allow with no stated end becomes a deny. ++func CompleteDecision(envelope *DecisionEnvelope, request CheckRequest, completion DecisionCompletion) { ++ if envelope == nil { ++ return ++ } ++ if envelope.ContractVersion == "" { ++ envelope.ContractVersion = DecisionRecordContractV1 ++ } ++ if envelope.Provenance.InputClaimDigests == nil { ++ envelope.Provenance.InputClaimDigests = InputClaimDigests(request) ++ } ++ ApplyAllowLifetime(envelope, completion.AllowTTL, completion.Now) ++} ++ ++// ParseAllowTTL resolves a package-declared TTL. ok is false when the allow ++// would have no stated end. Invalid strings return an error so package ++// validation can reject them. ++func ParseAllowTTL(declared string) (time.Duration, error) { ++ trimmed := strings.TrimSpace(declared) ++ if trimmed == "" { ++ return DefaultAllowTTL, nil ++ } ++ if strings.EqualFold(trimmed, "none") { ++ return 0, nil ++ } ++ ttl, err := time.ParseDuration(trimmed) ++ if err != nil { ++ return 0, fmt.Errorf("allow_ttl %q is not a Go duration: %w", declared, err) ++ } ++ if ttl <= 0 { ++ return 0, nil ++ } ++ return ttl, nil ++} ++ ++// ApplyAllowLifetime sets DecisionTime and, for allows, an explicit TTL. A ++// missing or zero TTL denies the allow rather than mint a standing grant. ++func ApplyAllowLifetime(envelope *DecisionEnvelope, declaredTTL string, now time.Time) { ++ if envelope == nil { ++ return ++ } ++ if now.IsZero() { ++ now = time.Now().UTC() ++ } else { ++ now = now.UTC() ++ } ++ if envelope.Provenance.DecisionTime == "" { ++ envelope.Provenance.DecisionTime = now.Format(time.RFC3339) ++ } ++ if envelope.Effect != DecisionEffectAllow { ++ return ++ } ++ ttl, err := ParseAllowTTL(declaredTTL) ++ if err != nil || ttl <= 0 { ++ if envelope.Diagnostics == nil { ++ envelope.Diagnostics = map[string]any{} ++ } ++ if envelope.Reason != "" { ++ envelope.Diagnostics["unstated_allow_reason"] = envelope.Reason ++ } ++ envelope.Effect = DecisionEffectDeny ++ envelope.Reason = ReasonAllowLifetimeUnstated ++ envelope.MatchedRule = ReasonAllowLifetimeUnstated ++ envelope.Lifetime = nil ++ return ++ } ++ display := strings.TrimSpace(declaredTTL) ++ if display == "" { ++ display = "15m" ++ } ++ envelope.Lifetime = &DecisionLifetime{ ++ Kind: DecisionLifetimeTTL, ++ TTL: display, ++ NotBefore: now.Format(time.RFC3339), ++ ExpiresAt: now.Add(ttl).Format(time.RFC3339), ++ } ++} ++ ++// ActionAuthorizationStatus is the lifecycle state of a durable authorization. ++type ActionAuthorizationStatus string ++ ++const ( ++ ActionAuthorizationPending ActionAuthorizationStatus = "pending" ++ ActionAuthorizationApproved ActionAuthorizationStatus = "approved" ++ ActionAuthorizationDenied ActionAuthorizationStatus = "denied" ++ ActionAuthorizationSuperseded ActionAuthorizationStatus = "superseded" ++ ActionAuthorizationExpired ActionAuthorizationStatus = "expired" ++ ActionAuthorizationRevoked ActionAuthorizationStatus = "revoked" ++) ++ ++// ActionAuthorization joins a durable approval lifecycle to one exact ++// flex-auth request and decision. Storage and approval collection remain the ++// responsibility of the organizational decision authority. ++type ActionAuthorization struct { ++ SchemaVersion string `json:"schema_version" yaml:"schema_version"` ++ ID string `json:"id" yaml:"id"` ++ Status ActionAuthorizationStatus `json:"status" yaml:"status"` ++ SupersededBy string `json:"superseded_by,omitempty" yaml:"superseded_by,omitempty"` ++ Request CheckRequest `json:"request" yaml:"request"` ++ Validity ActionAuthorizationValidity `json:"validity" yaml:"validity"` ++ Approvals ActionAuthorizationApprovals `json:"approvals" yaml:"approvals"` ++ Decision DecisionEnvelope `json:"decision" yaml:"decision"` ++ Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"` ++} ++ ++// ActionAuthorizationValidity bounds execution of an approved action. ++type ActionAuthorizationValidity struct { ++ NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"` ++ ExpiresAt string `json:"expires_at" yaml:"expires_at"` ++} ++ ++// ActionAuthorizationApprovals declares the approval threshold and evidence. ++type ActionAuthorizationApprovals struct { ++ RequiredCount int `json:"required_count" yaml:"required_count"` ++ Entries []ActionAuthorizationApprovalEntry `json:"entries" yaml:"entries"` ++} ++ ++// ActionAuthorizationApprovalEntry is one authenticated approver's evidence. ++type ActionAuthorizationApprovalEntry struct { ++ SubjectID string `json:"subject_id" yaml:"subject_id"` ++ ApprovedAt string `json:"approved_at" yaml:"approved_at"` ++ Assurance string `json:"assurance,omitempty" yaml:"assurance,omitempty"` ++ EvidenceRef string `json:"evidence_ref,omitempty" yaml:"evidence_ref,omitempty"` ++} ++ ++// Obligation describes a follow-up behavior required by a decision. ++type Obligation struct { ++ Type string `json:"type" yaml:"type"` ++ Parameters map[string]any `json:"parameters,omitempty" yaml:"parameters,omitempty"` ++} ++ ++// DecisionProvenance captures evaluator and policy provenance. ++type DecisionProvenance struct { ++ Evaluator string `json:"evaluator" yaml:"evaluator"` ++ Mode string `json:"mode" yaml:"mode"` ++ PolicyPackage string `json:"policy_package,omitempty" yaml:"policy_package,omitempty"` ++ PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"` ++ PolicyPackageDigest string `json:"policy_package_digest,omitempty" yaml:"policy_package_digest,omitempty"` ++ RegistrySnapshotDigest string `json:"registry_snapshot_digest,omitempty" yaml:"registry_snapshot_digest,omitempty"` ++ DirectoryETag string `json:"directory_etag,omitempty" yaml:"directory_etag,omitempty"` ++ InputClaimDigests map[string]string `json:"input_claim_digests,omitempty" yaml:"input_claim_digests,omitempty"` ++ DecisionTime string `json:"decision_time,omitempty" yaml:"decision_time,omitempty"` ++ ApprovalObservation *ApprovalObservationProvenance `json:"approval_observation,omitempty" yaml:"approval_observation,omitempty"` ++ Caller *CallerProvenance `json:"caller,omitempty" yaml:"caller,omitempty"` ++} ++ ++// ApprovalObservationProvenance binds fresh server-owned fact evidence without ++// rewriting the original carried claim/context or either PDP request digest. ++type ApprovalObservationProvenance struct { ++ SourceID string `json:"source_id"` ++ SourceRevision string `json:"source_revision"` ++ ConfigurationDigest string `json:"configuration_digest"` ++ ClaimDigest string `json:"claim_digest"` ++ NativeBindingDigest string `json:"native_binding_digest"` ++ ObservedAt string `json:"observed_at"` ++ NotAfter string `json:"not_after"` ++ ValidatedAt string `json:"validated_at"` ++} ++ ++// CallerProvenance records how the request was authenticated to flex-auth. ++// It is not decision material: the same request from a different authenticated ++// caller must decide identically. Mode is required. Under disabled the object ++// is {"mode":"disabled"} with no principal (FLEX-DEC-2026-009). ++type CallerProvenance struct { ++ Mode string `json:"mode" yaml:"mode"` ++ Principal string `json:"principal,omitempty" yaml:"principal,omitempty"` ++ Audience string `json:"audience,omitempty" yaml:"audience,omitempty"` ++ NotAfter string `json:"not_after,omitempty" yaml:"not_after,omitempty"` ++} ++ ++// CaringDecisionMetadata carries CARING descriptor and conformance details in ++// a decision envelope. ++type CaringDecisionMetadata struct { ++ Profile string `json:"profile" yaml:"profile"` ++ Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ RestrictionsEvaluated []Restriction `json:"restrictions_evaluated,omitempty" yaml:"restrictions_evaluated,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++ ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++} ++ ++// AuditEvent is the local log shape for decisions and exposure events. ++type AuditEvent struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type" yaml:"type"` ++ DecisionID string `json:"decision_id,omitempty" yaml:"decision_id,omitempty"` ++ Subject SubjectRef `json:"subject" yaml:"subject"` ++ Resource ResourceRef `json:"resource,omitempty" yaml:"resource,omitempty"` ++ Action string `json:"action,omitempty" yaml:"action,omitempty"` ++ Effect DecisionEffect `json:"effect,omitempty" yaml:"effect,omitempty"` ++ Timestamp string `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` ++ ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/caring.go +@@ -0,0 +1,335 @@ ++package api ++ ++// CaringProfileCaring040RC2 is the executable profile identifier for the ++// CARING 0.4.0-RC2 standard pinned by flex-auth. ++const CaringProfileCaring040RC2 = "caring-0.4.0-rc2" ++ ++// SubjectType is the CARING subject dimension. ++type SubjectType string ++ ++const ( ++ SubjectTypeHuman SubjectType = "Human" ++ SubjectTypeGroup SubjectType = "Group" ++ SubjectTypeOrganization SubjectType = "Organization" ++ SubjectTypeService SubjectType = "Service" ++ SubjectTypeAutomation SubjectType = "Automation" ++ SubjectTypeAgent SubjectType = "Agent" ++ SubjectTypeSystem SubjectType = "System" ++ SubjectTypeDevice SubjectType = "Device" ++ SubjectTypeProcess SubjectType = "Process" ++ SubjectTypeAnonymous SubjectType = "Anonymous" ++ SubjectTypeUnknown SubjectType = "Unknown" ++) ++ ++// OrganizationRelation is the CARING organization-relation dimension. ++type OrganizationRelation string ++ ++const ( ++ OrganizationRelationVendor OrganizationRelation = "Vendor" ++ OrganizationRelationServiceProvider OrganizationRelation = "ServiceProvider" ++ OrganizationRelationDistributor OrganizationRelation = "Distributor" ++ OrganizationRelationConsultant OrganizationRelation = "Consultant" ++ OrganizationRelationCustomer OrganizationRelation = "Customer" ++ OrganizationRelationCommunity OrganizationRelation = "Community" ++ OrganizationRelationAuthority OrganizationRelation = "Authority" ++ OrganizationRelationUnknown OrganizationRelation = "Unknown" ++) ++ ++// CanonicalRole is the CARING lifecycle responsibility posture. ++type CanonicalRole string ++ ++const ( ++ CanonicalRoleCreator CanonicalRole = "Creator" ++ CanonicalRoleBuilder CanonicalRole = "Builder" ++ CanonicalRoleVerifier CanonicalRole = "Verifier" ++ CanonicalRoleMaintainer CanonicalRole = "Maintainer" ++ CanonicalRoleIntegrator CanonicalRole = "Integrator" ++ CanonicalRoleOperator CanonicalRole = "Operator" ++ CanonicalRoleManager CanonicalRole = "Manager" ++ CanonicalRoleCoach CanonicalRole = "Coach" ++ CanonicalRoleDoer CanonicalRole = "Doer" ++) ++ ++// ScopeLevel is the CARING scope ladder. ++type ScopeLevel string ++ ++const ( ++ ScopeLevelEcosystem ScopeLevel = "Ecosystem" ++ ScopeLevelProduct ScopeLevel = "Product" ++ ScopeLevelPlatform ScopeLevel = "Platform" ++ ScopeLevelCluster ScopeLevel = "Cluster" ++ ScopeLevelEnvironment ScopeLevel = "Environment" ++ ScopeLevelTenant ScopeLevel = "Tenant" ++ ScopeLevelNamespace ScopeLevel = "Namespace" ++ ScopeLevelDomain ScopeLevel = "Domain" ++ ScopeLevelWorkspace ScopeLevel = "Workspace" ++ ScopeLevelProject ScopeLevel = "Project" ++ ScopeLevelProcess ScopeLevel = "Process" ++ ScopeLevelDataset ScopeLevel = "Dataset" ++ ScopeLevelResource ScopeLevel = "Resource" ++ ScopeLevelSubresource ScopeLevel = "Subresource" ++ ScopeLevelRecord ScopeLevel = "Record" ++ ScopeLevelField ScopeLevel = "Field" ++ ScopeLevelAction ScopeLevel = "Action" ++) ++ ++// Plane is the CARING access-surface dimension. ++type Plane string ++ ++const ( ++ PlaneIntent Plane = "Intent" ++ PlaneBuild Plane = "Build" ++ PlaneRuntime Plane = "Runtime" ++ PlaneExecution Plane = "Execution" ++ PlaneConfiguration Plane = "Configuration" ++ PlaneData Plane = "Data" ++ PlaneIdentity Plane = "Identity" ++ PlanePolicy Plane = "Policy" ++ PlaneSecret Plane = "Secret" ++ PlaneAudit Plane = "Audit" ++ PlaneCommercial Plane = "Commercial" ++ PlaneCommunity Plane = "Community" ++) ++ ++// Capability is a CARING capability verb. ++type Capability string ++ ++const ( ++ CapabilityView Capability = "View" ++ CapabilityViewCollection Capability = "ViewCollection" ++ CapabilityObserve Capability = "Observe" ++ CapabilityCreate Capability = "Create" ++ CapabilityEditOwn Capability = "EditOwn" ++ CapabilityEditAssigned Capability = "EditAssigned" ++ CapabilityEditAny Capability = "EditAny" ++ CapabilityDeleteOwn Capability = "DeleteOwn" ++ CapabilityDeleteAny Capability = "DeleteAny" ++ CapabilityBulkDelete Capability = "BulkDelete" ++ CapabilitySubmit Capability = "Submit" ++ CapabilityComment Capability = "Comment" ++ CapabilityReview Capability = "Review" ++ CapabilityApprove Capability = "Approve" ++ CapabilityReject Capability = "Reject" ++ CapabilityPublish Capability = "Publish" ++ CapabilityArchive Capability = "Archive" ++ CapabilityRestore Capability = "Restore" ++ CapabilityExecute Capability = "Execute" ++ CapabilityConfigure Capability = "Configure" ++ CapabilityOperate Capability = "Operate" ++ CapabilityDeploy Capability = "Deploy" ++ CapabilityIntegrate Capability = "Integrate" ++ CapabilityGrant Capability = "Grant" ++ CapabilityRevoke Capability = "Revoke" ++ CapabilityDelegate Capability = "Delegate" ++ CapabilityImpersonate Capability = "Impersonate" ++ CapabilityExport Capability = "Export" ++ CapabilityImport Capability = "Import" ++ CapabilityReplicate Capability = "Replicate" ++ CapabilityEncrypt Capability = "Encrypt" ++ CapabilityDecrypt Capability = "Decrypt" ++ CapabilityMask Capability = "Mask" ++ CapabilityInspect Capability = "Inspect" ++ CapabilityAudit Capability = "Audit" ++ CapabilityOverride Capability = "Override" ++ CapabilityEscalate Capability = "Escalate" ++ CapabilityBind Capability = "Bind" ++ CapabilityUse Capability = "Use" ++) ++ ++// ExposureMode describes how much information becomes visible or extractable. ++type ExposureMode string ++ ++const ( ++ ExposureModeNone ExposureMode = "None" ++ ExposureModeMetadata ExposureMode = "Metadata" ++ ExposureModeMasked ExposureMode = "Masked" ++ ExposureModeAggregated ExposureMode = "Aggregated" ++ ExposureModeSynthetic ExposureMode = "Synthetic" ++ ExposureModePseudonymous ExposureMode = "Pseudonymous" ++ ExposureModeEncrypted ExposureMode = "Encrypted" ++ ExposureModePlaintext ExposureMode = "Plaintext" ++ ExposureModeSecretMaterial ExposureMode = "SecretMaterial" ++ ExposureModeExportable ExposureMode = "Exportable" ++ ExposureModeCrossTenantAggregate ExposureMode = "CrossTenantAggregate" ++) ++ ++// Condition is a CARING runtime or governance condition. ++type Condition string ++ ++const ( ++ ConditionMFARequired Condition = "MFARequired" ++ ConditionDeviceTrusted Condition = "DeviceTrusted" ++ ConditionNetworkTrusted Condition = "NetworkTrusted" ++ ConditionTicketRequired Condition = "TicketRequired" ++ ConditionTenantConsentRequired Condition = "TenantConsentRequired" ++ ConditionCustomerApprovalRequired Condition = "CustomerApprovalRequired" ++ ConditionDualApprovalRequired Condition = "DualApprovalRequired" ++ ConditionTimeLimited Condition = "TimeLimited" ++ ConditionBusinessHoursOnly Condition = "BusinessHoursOnly" ++ ConditionEmergencyOnly Condition = "EmergencyOnly" ++ ConditionTrainingRequired Condition = "TrainingRequired" ++ ConditionContractRequired Condition = "ContractRequired" ++ ConditionNDARequired Condition = "NDARequired" ++ ConditionPurposeBound Condition = "PurposeBound" ++ ConditionCaseBound Condition = "CaseBound" ++ ConditionEnvironmentBound Condition = "EnvironmentBound" ++ ConditionNamespaceBound Condition = "NamespaceBound" ++ ConditionPipelineBound Condition = "PipelineBound" ++ ConditionChangeWindowBound Condition = "ChangeWindowBound" ++ ConditionLogged Condition = "Logged" ++ ConditionRecorded Condition = "Recorded" ++ ConditionNotificationRequired Condition = "NotificationRequired" ++ ConditionPostReviewRequired Condition = "PostReviewRequired" ++ ConditionHumanReviewRequired Condition = "HumanReviewRequired" ++ ConditionPolicyReviewRequired Condition = "PolicyReviewRequired" ++ ConditionWorkloadIdentityRequired Condition = "WorkloadIdentityRequired" ++) ++ ++// LifecycleState describes why access exists now. ++type LifecycleState string ++ ++const ( ++ LifecycleStateDesign LifecycleState = "Design" ++ LifecycleStateBuild LifecycleState = "Build" ++ LifecycleStateTest LifecycleState = "Test" ++ LifecycleStateReview LifecycleState = "Review" ++ LifecycleStateRelease LifecycleState = "Release" ++ LifecycleStateOnboard LifecycleState = "Onboard" ++ LifecycleStateIntegrate LifecycleState = "Integrate" ++ LifecycleStateMigrate LifecycleState = "Migrate" ++ LifecycleStateOperate LifecycleState = "Operate" ++ LifecycleStateSupport LifecycleState = "Support" ++ LifecycleStateImprove LifecycleState = "Improve" ++ LifecycleStateDeprecate LifecycleState = "Deprecate" ++ LifecycleStateArchive LifecycleState = "Archive" ++ LifecycleStateIncident LifecycleState = "Incident" ++ LifecycleStateLegal LifecycleState = "Legal" ++ LifecycleStateTerminate LifecycleState = "Terminate" ++) ++ ++// Restriction is an overriding CARING deny or limiting policy effect. ++type Restriction string ++ ++const ( ++ RestrictionNoAccess Restriction = "NoAccess" ++ RestrictionSuspended Restriction = "Suspended" ++ RestrictionTerminated Restriction = "Terminated" ++ RestrictionQuarantined Restriction = "Quarantined" ++ RestrictionScopeExcluded Restriction = "ScopeExcluded" ++ RestrictionDataClassRestricted Restriction = "DataClassRestricted" ++ RestrictionLegalHold Restriction = "LegalHold" ++ RestrictionExportBlocked Restriction = "ExportBlocked" ++ RestrictionImpersonationBlocked Restriction = "ImpersonationBlocked" ++ RestrictionCrossTenantBlocked Restriction = "CrossTenantBlocked" ++ RestrictionSecretAccessBlocked Restriction = "SecretAccessBlocked" ++ RestrictionPolicyFrozen Restriction = "PolicyFrozen" ++ RestrictionEmergencyLocked Restriction = "EmergencyLocked" ++ RestrictionRiskDenied Restriction = "RiskDenied" ++ RestrictionExecutionBlocked Restriction = "ExecutionBlocked" ++ RestrictionWorkloadCreationBlocked Restriction = "WorkloadCreationBlocked" ++ RestrictionPrivilegeEscalationBlocked Restriction = "PrivilegeEscalationBlocked" ++) ++ ++// ExposureEventType is a CARING exceptional or irregular access class. ++type ExposureEventType string ++ ++const ( ++ ExposureEventSupport ExposureEventType = "X-Support" ++ ExposureEventBreakGlass ExposureEventType = "X-BreakGlass" ++ ExposureEventSecurityTest ExposureEventType = "X-SecurityTest" ++ ExposureEventIncident ExposureEventType = "X-Incident" ++ ExposureEventLegalDemand ExposureEventType = "X-LegalDemand" ++ ExposureEventComplianceAudit ExposureEventType = "X-ComplianceAudit" ++ ExposureEventMigration ExposureEventType = "X-Migration" ++ ExposureEventRecovery ExposureEventType = "X-Recovery" ++ ExposureEventAdversarial ExposureEventType = "X-Adversarial" ++ ExposureEventMisconfiguration ExposureEventType = "X-Misconfiguration" ++ ExposureEventInducedAccess ExposureEventType = "X-InducedAccess" ++ ExposureEventPrivilegeEscalation ExposureEventType = "X-PrivilegeEscalation" ++) ++ ++// AccessPath describes how access is exercised. ++type AccessPath string ++ ++const ( ++ AccessPathDirect AccessPath = "direct" ++ AccessPathDelegated AccessPath = "delegated" ++ AccessPathMediated AccessPath = "mediated" ++ AccessPathInduced AccessPath = "induced" ++) ++ ++// CaringScope identifies where a CARING descriptor applies. ++type CaringScope struct { ++ Level ScopeLevel `json:"level" yaml:"level"` ++ ID string `json:"id" yaml:"id"` ++ Parent string `json:"parent,omitempty" yaml:"parent,omitempty"` ++ Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` ++ Resource string `json:"resource,omitempty" yaml:"resource,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// CaringAccessDescriptor is the executable flex-auth representation of a ++// CARING access assignment. ++type CaringAccessDescriptor struct { ++ ID string `json:"id,omitempty" yaml:"id,omitempty"` ++ Profile string `json:"profile" yaml:"profile"` ++ SubjectType SubjectType `json:"subject_type" yaml:"subject_type"` ++ OrganizationRelation OrganizationRelation `json:"organization_relation" yaml:"organization_relation"` ++ CanonicalRole CanonicalRole `json:"canonical_role" yaml:"canonical_role"` ++ Scope CaringScope `json:"scope" yaml:"scope"` ++ Planes []Plane `json:"planes" yaml:"planes"` ++ Capabilities []Capability `json:"capabilities" yaml:"capabilities"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"` ++ LifecycleState LifecycleState `json:"lifecycle_state,omitempty" yaml:"lifecycle_state,omitempty"` ++ Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"` ++ ExposureEvent ExposureEventType `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ AccessPath AccessPath `json:"access_path,omitempty" yaml:"access_path,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// CaringDerivedCapability records effective authority created by another grant. ++type CaringDerivedCapability struct { ++ Capability Capability `json:"capability" yaml:"capability"` ++ Reason string `json:"reason" yaml:"reason"` ++ Source string `json:"source,omitempty" yaml:"source,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++} ++ ++// CaringConformanceFinding is a diagnostic emitted by descriptive or ++// prescriptive CARING validation. ++type CaringConformanceFinding struct { ++ Code string `json:"code" yaml:"code"` ++ Severity string `json:"severity" yaml:"severity"` ++ Message string `json:"message" yaml:"message"` ++ Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"` ++ Descriptor string `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// CaringExposureEvent records exceptional or irregular information exposure. ++type CaringExposureEvent struct { ++ ID string `json:"id" yaml:"id"` ++ Type ExposureEventType `json:"type" yaml:"type"` ++ Actor string `json:"actor" yaml:"actor"` ++ Subject string `json:"subject" yaml:"subject"` ++ Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"` ++ Scope *CaringScope `json:"scope,omitempty" yaml:"scope,omitempty"` ++ Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"` ++ CapabilitiesUsed []Capability `json:"capabilities_used,omitempty" yaml:"capabilities_used,omitempty"` ++ DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"` ++ ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"` ++ Reason string `json:"reason" yaml:"reason"` ++ AuthoritySource string `json:"authority_source,omitempty" yaml:"authority_source,omitempty"` ++ Approval string `json:"approval,omitempty" yaml:"approval,omitempty"` ++ StartTime string `json:"start_time,omitempty" yaml:"start_time,omitempty"` ++ EndTime string `json:"end_time,omitempty" yaml:"end_time,omitempty"` ++ ResourcesAccessed []string `json:"resources_accessed,omitempty" yaml:"resources_accessed,omitempty"` ++ Evidence []string `json:"evidence,omitempty" yaml:"evidence,omitempty"` ++ NotificationStatus string `json:"notification_status,omitempty" yaml:"notification_status,omitempty"` ++ PostReview string `json:"post_review,omitempty" yaml:"post_review,omitempty"` ++ ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/api/resource_manifest.go +@@ -0,0 +1,32 @@ ++package api ++ ++// ResourceManifest is the shape a protected system publishes to register ++// its resources with flex-auth. The shape is pinned against the ++// Markitect-side emitter in markitect-tool (MKTT-WP-0014); see ++// schemas/resource_manifest.schema.json for the JSON Schema and ++// examples/markitect/resource_manifest.yaml for the canonical example. ++type ResourceManifest struct { ++ ID string `json:"id" yaml:"id"` ++ System string `json:"system" yaml:"system"` ++ Resources []Resource `json:"resources" yaml:"resources"` ++ Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"` ++ CaringProfile string `json:"caring_profile,omitempty" yaml:"caring_profile,omitempty"` ++ Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"` ++} ++ ++// Resource is one entry in a ResourceManifest. ++type Resource struct { ++ ID string `json:"id" yaml:"id"` ++ Type string `json:"type" yaml:"type"` ++ Path string `json:"path,omitempty" yaml:"path,omitempty"` ++ Parent string `json:"parent,omitempty" yaml:"parent,omitempty"` ++ Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"` ++ TrustZone string `json:"trust_zone,omitempty" yaml:"trust_zone,omitempty"` ++ Owner string `json:"owner,omitempty" yaml:"owner,omitempty"` ++ Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"` ++ Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"` ++} ++ ++// FlexAuthContractV0 is the metadata.flex_auth_contract value that ++// signals the v0 resource-registration contract. ++const FlexAuthContractV0 = "resource-registration-v0" +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/decisionpep/client.go +@@ -0,0 +1,296 @@ ++// Package decisionpep authenticates the producer's decision record for one ++// current request. It never evaluates policy, caches a verdict, or discovers trust. ++package decisionpep ++ ++import ( ++ "bytes" ++ "context" ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/tls" ++ "crypto/x509" ++ "encoding/base64" ++ "encoding/hex" ++ "encoding/json" ++ "errors" ++ "io" ++ "net/http" ++ "net/url" ++ "os" ++ "path/filepath" ++ "strings" ++ "time" ++ ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++var ErrRefused = errors.New("authenticated decision refused") ++ ++const nonceKey = "decisionpep_request_nonce" ++ ++const JSONProfile = "lossless-json-v1" ++const provenanceMode = "standalone/lossless-json-v1" ++const maxBytes = 2 << 20 ++ ++// Admission is public, independently admitted owner configuration. TokenFile is ++// only a path to a private rotating caller credential, never a literal token. ++type Admission struct { ++ Endpoint string `json:"endpoint"` ++ CAFile string `json:"ca_file"` ++ TokenFile string `json:"token_file"` ++ Keys map[string]string `json:"keys"` ++ Evaluator string `json:"evaluator"` ++ PolicyPackage string `json:"policy_package"` ++ PolicyVersion string `json:"policy_version"` ++ PolicyDigest string `json:"policy_digest"` ++ RegistryDigest string `json:"registry_digest"` ++ CallerPrincipal string `json:"caller_principal"` ++ CallerAudience string `json:"caller_audience"` ++ MaxTTL string `json:"max_ttl"` ++ MaxClockSkew string `json:"max_clock_skew"` ++} ++ ++type Client struct { ++ admission Admission ++ keys map[string]ed25519.PublicKey ++ http *http.Client ++ maxTTL, skew time.Duration ++ now func() time.Time ++} ++ ++// Load reads a current independently admitted file. No trust is bootstrapped ++// from a PDP response, token contents, discovery endpoint, or environment value. ++func Load(path string) (*Client, error) { ++ raw, err := readFile(path, false) ++ if err != nil { ++ return nil, ErrRefused ++ } ++ var a Admission ++ d := json.NewDecoder(bytes.NewReader(raw)) ++ d.DisallowUnknownFields() ++ if d.Decode(&a) != nil { ++ return nil, ErrRefused ++ } ++ if _, err = d.Token(); err != io.EOF { ++ return nil, ErrRefused ++ } ++ return New(a) ++} ++ ++func New(a Admission) (*Client, error) { ++ u, err := url.Parse(a.Endpoint) ++ if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Path != "/v1/check" || u.RawQuery != "" || u.Fragment != "" { ++ return nil, ErrRefused ++ } ++ if !filepath.IsAbs(a.CAFile) || !filepath.IsAbs(a.TokenFile) || a.Evaluator == "" || a.PolicyPackage == "" || a.PolicyVersion == "" || a.CallerPrincipal == "" || a.CallerAudience == "" { ++ return nil, ErrRefused ++ } ++ if !validDigest(a.PolicyDigest) || !validDigest(a.RegistryDigest) || len(a.Keys) == 0 { ++ return nil, ErrRefused ++ } ++ ttl, err := time.ParseDuration(a.MaxTTL) ++ if err != nil || ttl <= 0 { ++ return nil, ErrRefused ++ } ++ skew, err := time.ParseDuration(a.MaxClockSkew) ++ if err != nil || skew < 0 || skew > ttl { ++ return nil, ErrRefused ++ } ++ ca, err := readFile(a.CAFile, false) ++ if err != nil { ++ return nil, ErrRefused ++ } ++ roots := x509.NewCertPool() ++ if !roots.AppendCertsFromPEM(ca) { ++ return nil, ErrRefused ++ } ++ keys := map[string]ed25519.PublicKey{} ++ for kid, value := range a.Keys { ++ b, e := base64.RawURLEncoding.Strict().DecodeString(value) ++ if e != nil || kid == "" || len(b) != ed25519.PublicKeySize { ++ return nil, ErrRefused ++ } ++ keys[kid] = append(ed25519.PublicKey(nil), b...) ++ } ++ transport := &http.Transport{Proxy: nil, TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}, DisableKeepAlives: true} ++ client := &http.Client{Transport: transport, Timeout: 10 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return ErrRefused }} ++ return &Client{admission: a, keys: keys, http: client, maxTTL: ttl, skew: skew, now: time.Now}, nil ++} ++ ++func validDigest(value string) bool { ++ if !strings.HasPrefix(value, "sha256:") || len(value) != 71 { ++ return false ++ } ++ _, e := hex.DecodeString(value[7:]) ++ return e == nil && strings.ToLower(value) == value ++} ++ ++func readFile(path string, private bool) ([]byte, error) { ++ if !filepath.IsAbs(path) { ++ return nil, ErrRefused ++ } ++ f, e := os.Open(path) ++ if e != nil { ++ return nil, ErrRefused ++ } ++ defer f.Close() ++ info, e := f.Stat() ++ if e != nil || !info.Mode().IsRegular() || info.Mode().Perm()&0022 != 0 { ++ return nil, ErrRefused ++ } ++ if private && info.Mode().Perm()&0007 != 0 { ++ return nil, ErrRefused ++ } ++ b, e := io.ReadAll(io.LimitReader(f, maxBytes+1)) ++ if e != nil || len(b) > maxBytes { ++ return nil, ErrRefused ++ } ++ return b, nil ++} ++ ++// Check sends exactly one request with a cryptographically fresh nonce. The ++// returned envelope is the producer's authenticated typed interpretation. ++// Callers must execute only the request's exact operation and honor their own ++// execution boundary; a login decision is never a later resource decision. ++func (c *Client) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) { ++ refuse := func() (api.DecisionEnvelope, error) { return api.DecisionEnvelope{}, ErrRefused } ++ if c == nil || request.Purpose != "" || request.Tenant == "" || request.Subject.ID == "" || request.Subject.Type == "" || request.Resource.ID == "" || request.Resource.Type == "" || request.Resource.System == "" || request.Action == "" { ++ return refuse() ++ } ++ if request.Subject.Tenant != request.Tenant || request.Resource.Tenant != request.Tenant { ++ return refuse() ++ } ++ if request.PolicyVersion != "" && request.PolicyVersion != c.admission.PolicyVersion { ++ return refuse() ++ } ++ // Use the producer's typed representation and preserve numeric lexemes. A ++ // producer that rounds before hashing fails this join rather than weakening it. ++ raw, err := json.Marshal(request) ++ if err != nil { ++ return refuse() ++ } ++ decoder := json.NewDecoder(bytes.NewReader(raw)) ++ decoder.UseNumber() ++ if decoder.Decode(&request) != nil { ++ return refuse() ++ } ++ if request.Context == nil { ++ request.Context = map[string]any{} ++ } ++ if _, exists := request.Context[nonceKey]; exists { ++ return refuse() ++ } ++ nonce := make([]byte, 32) ++ if _, err = rand.Read(nonce); err != nil { ++ return refuse() ++ } ++ request.Context[nonceKey] = hex.EncodeToString(nonce) ++ request.Context["decisionpep_json_profile"] = JSONProfile ++ request.ID = hex.EncodeToString(nonce) ++ request.PolicyVersion = c.admission.PolicyVersion ++ raw, err = json.Marshal(request) ++ if err != nil || len(raw) > maxBytes { ++ return refuse() ++ } ++ token, err := readFile(c.admission.TokenFile, true) ++ if err != nil { ++ return refuse() ++ } ++ value := strings.TrimSpace(string(token)) ++ if value == "" || strings.ContainsAny(value, " \t\r\n") { ++ return refuse() ++ } ++ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.admission.Endpoint, bytes.NewReader(raw)) ++ if err != nil { ++ return refuse() ++ } ++ req.Header.Set("Content-Type", "application/json") ++ req.Header.Set("Access-Engine-JSON-Profile", JSONProfile) ++ req.Header.Set("Authorization", "Bearer "+value) ++ started := c.now().UTC() ++ response, err := c.http.Do(req) ++ if err != nil { ++ return refuse() ++ } ++ defer response.Body.Close() ++ if response.StatusCode != http.StatusOK { ++ return refuse() ++ } ++ body, err := io.ReadAll(io.LimitReader(response.Body, maxBytes+1)) ++ if err != nil || len(body) > maxBytes { ++ return refuse() ++ } ++ envelope, err := sign.DecodeEnvelope(body) ++ if err != nil || sign.Verify(envelope, c.keys) != nil { ++ return refuse() ++ } ++ if c.validate(envelope, request, started, c.now().UTC()) != nil { ++ return refuse() ++ } ++ return envelope, nil ++} ++ ++func (c *Client) validate(e api.DecisionEnvelope, r api.CheckRequest, started, now time.Time) error { ++ p := e.Provenance ++ b := e.Binding ++ l := e.Lifetime ++ if e.ID == "" || e.ContractVersion != api.DecisionRecordContractV1 || e.Effect != api.DecisionEffectAllow || e.RequestID != r.ID || len(e.Obligations) != 0 || b == nil || l == nil { ++ return ErrRefused ++ } ++ if p.Evaluator != c.admission.Evaluator || p.Mode != provenanceMode || p.PolicyPackage != c.admission.PolicyPackage || p.PolicyVersion != c.admission.PolicyVersion || p.PolicyPackageDigest != c.admission.PolicyDigest || p.RegistrySnapshotDigest != c.admission.RegistryDigest || e.MatchedPolicyVersion != p.PolicyVersion { ++ return ErrRefused ++ } ++ if b.Purpose != "" || b.SubmittedRequestDigest != api.RequestDigest(r) || b.Tenant != r.Tenant || b.Action != r.Action || api.CanonicalDigest(b.Context) != api.CanonicalDigest(r.Context) { ++ return ErrRefused ++ } ++ if !sameSubject(b.Subject, r.Subject) || !sameResource(b.Resource, r.Resource) || !sameSubject(e.Subject, b.Subject) || !sameResource(e.Resource, b.Resource) { ++ return ErrRefused ++ } ++ evaluated := api.CheckRequest{Tenant: b.Tenant, Subject: b.Subject, Action: b.Action, Resource: b.Resource, Context: b.Context, Purpose: b.Purpose} ++ if b.RequestDigest != api.RequestDigest(evaluated) { ++ return ErrRefused ++ } ++ for k, v := range api.InputClaimDigests(r) { ++ if p.InputClaimDigests[k] != v { ++ return ErrRefused ++ } ++ } ++ caller := p.Caller ++ if caller == nil || caller.Mode != "enforce" || caller.Principal != c.admission.CallerPrincipal || caller.Audience != c.admission.CallerAudience { ++ return ErrRefused ++ } ++ callerEnd, err := time.Parse(time.RFC3339Nano, caller.NotAfter) ++ if err != nil || !now.Before(callerEnd) { ++ return ErrRefused ++ } ++ decision, err := time.Parse(time.RFC3339Nano, p.DecisionTime) ++ if err != nil || decision.Before(started.Add(-c.skew)) || decision.After(now.Add(c.skew)) { ++ return ErrRefused ++ } ++ begin, err := time.Parse(time.RFC3339Nano, l.NotBefore) ++ if err != nil || begin.After(now) { ++ return ErrRefused ++ } ++ end, err := time.Parse(time.RFC3339Nano, l.ExpiresAt) ++ if err != nil || !now.Before(end) || !end.After(begin) || end.Sub(begin) > c.maxTTL || end.After(callerEnd) { ++ return ErrRefused ++ } ++ ttl, err := time.ParseDuration(l.TTL) ++ if err != nil || l.Kind != api.DecisionLifetimeTTL || ttl <= 0 || ttl > c.maxTTL { ++ return ErrRefused ++ } ++ // Producer CompleteDecision uses its exact decision instant for both bounds. ++ // RFC3339 output can truncate subsecond precision, so do not invent lost time. ++ if !begin.Equal(decision) || end.Sub(begin) != ttl { ++ return ErrRefused ++ } ++ return nil ++} ++ ++func sameSubject(a, b api.SubjectRef) bool { ++ return a.ID == b.ID && a.Type == b.Type && a.Tenant == b.Tenant ++} ++func sameResource(a, b api.ResourceRef) bool { ++ return a.ID == b.ID && a.Type == b.Type && a.System == b.System && a.Tenant == b.Tenant ++} +--- /dev/null ++++ b/third_party/access-engine-pep/pkg/decisionpep/client_test.go +@@ -0,0 +1,211 @@ ++package decisionpep ++ ++import ( ++ "context" ++ "crypto/ed25519" ++ "crypto/rand" ++ "crypto/x509" ++ "crypto/x509/pkix" ++ "encoding/base64" ++ "encoding/json" ++ "encoding/pem" ++ "io" ++ "math/big" ++ "net/http" ++ "os" ++ "path/filepath" ++ "strings" ++ "testing" ++ "time" ++ ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++type transportFunc func(*http.Request) (*http.Response, error) ++ ++func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } ++func testClient(t *testing.T) (*Client, ed25519.PrivateKey, time.Time) { ++ t.Helper() ++ pub, key, e := ed25519.GenerateKey(rand.Reader) ++ if e != nil { ++ t.Fatal(e) ++ } ++ now := time.Date(2026, 10, 18, 1, 2, 3, 0, time.UTC) ++ dir := t.TempDir() ++ cert, e := x509.CreateCertificate(rand.Reader, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, pub, key) ++ if e != nil { ++ t.Fatal(e) ++ } ++ ca := filepath.Join(dir, "ca.pem") ++ token := filepath.Join(dir, "token") ++ if e = os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0600); e != nil { ++ t.Fatal(e) ++ } ++ if e = os.WriteFile(token, []byte("synthetic-caller-token"), 0600); e != nil { ++ t.Fatal(e) ++ } ++ a := Admission{Endpoint: "https://pdp.invalid/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"test-key": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:test:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "1s"} ++ c, e := New(a) ++ if e != nil { ++ t.Fatal(e) ++ } ++ c.now = func() time.Time { return now } ++ return c, key, now ++} ++func testRequest() api.CheckRequest { ++ return api.CheckRequest{Tenant: "tenant:test", Subject: api.SubjectRef{ID: "subject:test", Type: api.SubjectType("Human"), Tenant: "tenant:test", Attributes: map[string]any{"groups": []string{"engineers"}}}, Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: "tenant:test"}, Context: map[string]any{"big": json.Number("9007199254740993"), "fraction": json.Number("0.123456789123456789")}} ++} ++func envelope(c *Client, r api.CheckRequest, key ed25519.PrivateKey, now time.Time) api.DecisionEnvelope { ++ e := api.DecisionEnvelope{ID: "test-decision", RequestID: r.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: c.admission.PolicyVersion, Subject: r.Subject, Resource: r.Resource, Binding: api.NewDecisionBindingFor(r, r), Provenance: api.DecisionProvenance{Evaluator: c.admission.Evaluator, Mode: provenanceMode, PolicyPackage: c.admission.PolicyPackage, PolicyVersion: c.admission.PolicyVersion, PolicyPackageDigest: c.admission.PolicyDigest, RegistrySnapshotDigest: c.admission.RegistryDigest, Caller: &api.CallerProvenance{Mode: "enforce", Principal: c.admission.CallerPrincipal, Audience: c.admission.CallerAudience, NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}} ++ api.CompleteDecision(&e, r, api.DecisionCompletion{AllowTTL: "10s", Now: now}) ++ if sign.Sign(&e, key, "test-key") != nil { ++ panic("test sign") ++ } ++ return e ++} ++func respond(t *testing.T, c *Client, key ed25519.PrivateKey, now time.Time, mutate func(*api.DecisionEnvelope), resign bool) { ++ t.Helper() ++ c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) { ++ if req.Header.Get("Access-Engine-JSON-Profile") != JSONProfile { ++ t.Fatal("missing lossless profile") ++ } ++ if req.Header.Get("Authorization") != "Bearer synthetic-caller-token" { ++ t.Fatal("wrong synthetic caller token") ++ } ++ var r api.CheckRequest ++ d := json.NewDecoder(req.Body) ++ d.UseNumber() ++ if d.Decode(&r) != nil { ++ t.Fatal("request decode") ++ } ++ if r.Context["big"] != json.Number("9007199254740993") || r.Context["fraction"] != json.Number("0.123456789123456789") { ++ t.Fatal("request numeric precision lost") ++ } ++ if len(r.Context[nonceKey].(string)) != 64 || r.ID != r.Context[nonceKey] { ++ t.Fatal("nonce") ++ } ++ e := envelope(c, r, key, now) ++ if mutate != nil { ++ mutate(&e) ++ } ++ if resign { ++ if sign.Sign(&e, key, "test-key") != nil { ++ t.Fatal("sign") ++ } ++ } ++ b, err := json.Marshal(e) ++ if err != nil { ++ t.Fatal(err) ++ } ++ return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(b))), Header: http.Header{}}, nil ++ })} ++} ++func TestAuthenticDecisionProducerCanonicalPrecision(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, nil, false) ++ e, err := c.Check(context.Background(), testRequest()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if e.Binding.Context["big"] != json.Number("9007199254740993") || e.Binding.Context["fraction"] != json.Number("0.123456789123456789") { ++ t.Fatal("signed typed precision lost") ++ } ++ if sign.Verify(e, c.keys) != nil { ++ t.Fatal("returned envelope not authentic producer bytes") ++ } ++} ++func TestDecisionAdverseBindings(t *testing.T) { ++ cases := map[string]func(*api.DecisionEnvelope){ ++ "unsigned": func(e *api.DecisionEnvelope) { e.Signature = api.UnsignedEnvelopeSignature() }, ++ "effect": func(e *api.DecisionEnvelope) { e.Effect = api.DecisionEffectDeny }, ++ "evaluator": func(e *api.DecisionEnvelope) { e.Provenance.Evaluator = "impostor" }, ++ "policy": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackage = "other" }, ++ "version": func(e *api.DecisionEnvelope) { e.Provenance.PolicyVersion = "v2" }, ++ "policy-digest": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64) }, ++ "registry": func(e *api.DecisionEnvelope) { ++ e.Provenance.RegistrySnapshotDigest = "sha256:" + strings.Repeat("4", 64) ++ }, ++ "tenant": func(e *api.DecisionEnvelope) { e.Binding.Tenant = "other" }, ++ "actor": func(e *api.DecisionEnvelope) { e.Binding.Subject.ID = "other" }, ++ "action": func(e *api.DecisionEnvelope) { e.Binding.Action = "write" }, ++ "resource": func(e *api.DecisionEnvelope) { e.Binding.Resource.ID = "other" }, ++ "purpose": func(e *api.DecisionEnvelope) { e.Binding.Purpose = "readback" }, ++ "submitted": func(e *api.DecisionEnvelope) { e.Binding.SubmittedRequestDigest = "wrong" }, ++ "context": func(e *api.DecisionEnvelope) { e.Binding.Context["fraction"] = json.Number("0.123456789123456788") }, ++ "legacy-numeric-profile": func(e *api.DecisionEnvelope) { e.Provenance.Mode = "standalone" }, ++ "caller-warn": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Mode = "warn" }, ++ "caller-principal": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Principal = "other" }, ++ "caller-audience": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Audience = "other" }, ++ "caller-expired": func(e *api.DecisionEnvelope) { e.Provenance.Caller.NotAfter = "2020-01-01T00:00:00Z" }, ++ "stale-decision": func(e *api.DecisionEnvelope) { e.Provenance.DecisionTime = "2020-01-01T00:00:00Z" }, ++ "expired": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2020-01-01T00:00:00Z" }, ++ "future-begin": func(e *api.DecisionEnvelope) { e.Lifetime.NotBefore = "2030-01-01T00:00:00Z" }, ++ "subnanosecond-expiry": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2026-10-18T01:02:02.999999999Z" }, ++ "missing-TTL": func(e *api.DecisionEnvelope) { e.Lifetime = nil }, ++ "excess-TTL": func(e *api.DecisionEnvelope) { e.Lifetime.TTL = "1h" }, ++ "obligation": func(e *api.DecisionEnvelope) { e.Obligations = []api.Obligation{{Type: "unimplemented"}} }, ++ } ++ for name, mutation := range cases { ++ t.Run(name, func(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, mutation, name != "unsigned") ++ if _, err := c.Check(context.Background(), testRequest()); err == nil { ++ t.Fatal("accepted wrong signed binding") ++ } ++ }) ++ } ++} ++func TestSignatureForgeryAndReplay(t *testing.T) { ++ t.Run("forged-payload", func(t *testing.T) { ++ c, key, now := testClient(t) ++ respond(t, c, key, now, func(e *api.DecisionEnvelope) { e.Reason = "changed after signature" }, false) ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("forgery accepted") ++ } ++ }) ++ t.Run("wrong-key", func(t *testing.T) { ++ c, _, now := testClient(t) ++ _, key, _ := ed25519.GenerateKey(rand.Reader) ++ respond(t, c, key, now, nil, false) ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("wrong key accepted") ++ } ++ }) ++ t.Run("replay", func(t *testing.T) { ++ c, key, now := testClient(t) ++ var saved []byte ++ c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) { ++ if saved == nil { ++ var r api.CheckRequest ++ d := json.NewDecoder(req.Body) ++ d.UseNumber() ++ if d.Decode(&r) != nil { ++ t.Fatal("decode") ++ } ++ e := envelope(c, r, key, now) ++ saved, _ = json.Marshal(e) ++ } ++ return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(saved)))}, nil ++ })} ++ if _, e := c.Check(context.Background(), testRequest()); e != nil { ++ t.Fatal(e) ++ } ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("old decision replay accepted for new nonce") ++ } ++ }) ++} ++func TestUnavailableAndPublicTokenFileRefuse(t *testing.T) { ++ c, _, _ := testClient(t) ++ c.http = &http.Client{Transport: transportFunc(func(*http.Request) (*http.Response, error) { return nil, io.ErrUnexpectedEOF })} ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("unavailable allowed") ++ } ++ if e := os.Chmod(c.admission.TokenFile, 0644); e != nil { ++ t.Fatal(e) ++ } ++ if _, e := c.Check(context.Background(), testRequest()); e == nil { ++ t.Fatal("public token file allowed") ++ } ++} +--- /dev/null ++++ b/third_party/access-engine-pep/producer-source.json +@@ -0,0 +1,30 @@ ++{ ++ "upstream": "github.com/netkingdom/access-engine", ++ "commit": "d328a2601095686f74376d87791bff01e2a3a2a3", ++ "exact_unchanged_files": { ++ "pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19", ++ "pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3", ++ "pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f", ++ "internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde", ++ "LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6" ++ }, ++ "module": "Standalone Go1.24 source slice with original module/import identity; no canonical/schema/signature modifications", ++ "new_code": [ ++ "pkg/decisionpep/client.go" ++ ], ++ "fixture_code": [ ++ "testsupport/signfixture/sign.go" ++ ], ++ "source_of_truth": "repository root; generated with tools/export_decisionpep_adapter.py", ++ "exported_file_sha256": { ++ "LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6", ++ "go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3", ++ "internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde", ++ "pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19", ++ "pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3", ++ "pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f", ++ "pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba", ++ "pkg/decisionpep/client_test.go": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62", ++ "testsupport/signfixture/sign.go": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29" ++ } ++} +--- /dev/null ++++ b/third_party/access-engine-pep/testsupport/signfixture/sign.go +@@ -0,0 +1,14 @@ ++// Package signfixture exposes producer signing only for synthetic consumer tests. ++// Callers supply test keys; this package contains no credentials or runtime signer. ++package signfixture ++ ++import ( ++ "crypto/ed25519" ++ "github.com/netkingdom/access-engine/internal/sign" ++ "github.com/netkingdom/access-engine/pkg/api" ++) ++ ++// Sign uses the unchanged producer envelope format with a caller-owned fixture key. ++func Sign(envelope *api.DecisionEnvelope, key ed25519.PrivateKey, kid string) error { ++ return sign.Sign(envelope, key, kid) ++} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/inputs.lock.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/inputs.lock.json new file mode 100644 index 0000000..d3259d0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/inputs.lock.json @@ -0,0 +1,206 @@ +{ + "schema": "railiance-apps.forgejo-release-inputs.v1", + "forgejo_version": "11.0.3", + "source_archive_sha256": "1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c", + "source_commit": "b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237", + "patch_sha256": "feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c", + "go_archive_sha256": "3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8", + "go_version": "1.24.3", + "node_archive_sha256": "dafe2e8f82cb97de1bd10db9e2ec4c07bbf53389b0799b1e095a918951e78fd4", + "node_version": "22.15.0", + "source_date_epoch": 1752105237, + "release_version": "11.0.3+cust80.feb10793", + "tags": "bindata timetzdata sqlite sqlite_unlock_notify", + "runtime_base": "code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b", + "runtime_index_digest": "sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba", + "platform": "linux/amd64", + "reviewed_outputs": [ + { + "path": "cmd/admin_auth_oauth.go", + "sha256": "d718d817c46f6393488a9c5a894b7535640cd0ec1a1ae3f72f8411a2bbec3b38" + }, + { + "path": "cmd/admin_auth_oauth_secret_linux.go", + "sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b" + }, + { + "path": "cmd/admin_auth_oauth_secret_linux_test.go", + "sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2" + }, + { + "path": "cmd/admin_auth_oauth_secret_other.go", + "sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d" + }, + { + "path": "go.mod", + "sha256": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a" + }, + { + "path": "models/auth/source_operation.go", + "sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31" + }, + { + "path": "models/auth/source_operation_internal_test.go", + "sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb" + }, + { + "path": "models/auth/source_operation_postgres_export_test.go", + "sha256": "b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d" + }, + { + "path": "models/auth/source_operation_postgres_test.go", + "sha256": "4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe" + }, + { + "path": "models/auth/source_operation_process_test.go", + "sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd" + }, + { + "path": "models/auth/source_operation_test.go", + "sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428" + }, + { + "path": "models/db/context.go", + "sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7" + }, + { + "path": "models/db/engine.go", + "sha256": "d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8" + }, + { + "path": "models/db/sensitive_sql.go", + "sha256": "06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584" + }, + { + "path": "models/db/sensitive_sql_logging_test.go", + "sha256": "d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7" + }, + { + "path": "models/db/sensitive_sql_test.go", + "sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b" + }, + { + "path": "models/db/tracing_context_test.go", + "sha256": "612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070" + }, + { + "path": "routers/web/admin/auths.go", + "sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923" + }, + { + "path": "routers/web/auth/oauth.go", + "sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17" + }, + { + "path": "services/auth/source/oauth2/access_engine.go", + "sha256": "d5f07f9491ac2212410e1fa8c0248d68ba3a723e7ca99e363627c5292d187aa8" + }, + { + "path": "services/auth/source/oauth2/access_engine_assurance.go", + "sha256": "42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642" + }, + { + "path": "services/auth/source/oauth2/access_engine_assurance_test.go", + "sha256": "44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3" + }, + { + "path": "services/auth/source/oauth2/access_engine_azp_test.go", + "sha256": "5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce" + }, + { + "path": "services/auth/source/oauth2/access_engine_callback_test.go", + "sha256": "5cea36b630c895a9473dc876b7128526ddef65d9395b2fddc56343e162452472" + }, + { + "path": "services/auth/source/oauth2/access_engine_test.go", + "sha256": "99edd6cfe3e834d73e0447a7915f52cc4ab7b82644669c5ae61963f850b447e8" + }, + { + "path": "services/auth/source/oauth2/providers_openid.go", + "sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf" + }, + { + "path": "services/auth/source/oauth2/required_claim_values.go", + "sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda" + }, + { + "path": "services/auth/source/oauth2/required_claim_values_test.go", + "sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0" + }, + { + "path": "services/auth/source/oauth2/source.go", + "sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642" + }, + { + "path": "services/auth/source/oauth2/source_callout.go", + "sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c" + }, + { + "path": "third_party/access-engine-pep/LICENSE", + "sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6" + }, + { + "path": "third_party/access-engine-pep/go.mod", + "sha256": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3" + }, + { + "path": "third_party/access-engine-pep/internal/sign/sign.go", + "sha256": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde" + }, + { + "path": "third_party/access-engine-pep/pkg/api/canonical.go", + "sha256": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19" + }, + { + "path": "third_party/access-engine-pep/pkg/api/caring.go", + "sha256": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3" + }, + { + "path": "third_party/access-engine-pep/pkg/api/resource_manifest.go", + "sha256": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f" + }, + { + "path": "third_party/access-engine-pep/pkg/decisionpep/client.go", + "sha256": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba" + }, + { + "path": "third_party/access-engine-pep/pkg/decisionpep/client_test.go", + "sha256": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62" + }, + { + "path": "third_party/access-engine-pep/producer-source.json", + "sha256": "c1d83e3d79f9860ffaa61ec19a6b3415e914d3058b11b07cdd05a7d1182cdceb" + }, + { + "path": "third_party/access-engine-pep/testsupport/signfixture/sign.go", + "sha256": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29" + } + ], + "lockfiles": { + "go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a", + "go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf", + "package.json": "ab5c54ec7077501b50acf3ab5f1fff4f63a51abe99e720b8ecdc6cc6c78f39fc", + "package-lock.json": "fdd5c2f891311be7c01b1907b418c243ca1f0f951fd4b4ee5b5e931c43493657" + }, + "host_tools": { + "/usr/bin/make": "d78b8f1d099fbcfb6f2f49ab87223b9b68fb3956642f92d6ec6de812e8afa965", + "/usr/bin/gcc": "1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26", + "/usr/bin/ld": "e46a122cc3a29feba3b1d0fc2a274b6e46538be016ebe381370c0801bb98b0b8", + "/usr/bin/ar": "534681ac11c18868cfc4fdf98770aa0ba8973eedc90c231e94e6ba96e1a04f27", + "/usr/bin/bwrap": "e318903862396f96de3df57264e0158682b952fd3fb53ac23d876413e7b30f71", + "/usr/lib/x86_64-linux-gnu/libc.a": "a40c7eae7581f977b65eddb81cbc799213a695749e039d966db55bdee4ac9621", + "/usr/lib/x86_64-linux-gnu/libm.a": "420cd3c406f6f8c12deb3d1291146e9c091ee015a7c39eaf2237f01e80d6f0ae" + }, + "runtime_base_config_sha256": "bc5a3df111729582faadebd01d126ba769e2b65d0c62f306f6bc79f4e562452b", + "patch_file": "forgejo-security-extension.patch", + "candidate_review_status": "coordinator tested; independent owner review pending", + "producer_source_commit": "ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3", + "optional_source_tests": { + "source_operation_postgres": { + "database_major": 16, + "synthetic_fixture_image": "postgres@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777", + "source_only": true, + "production_connection_or_credentials": false + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/runtime-base-config.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/runtime-base-config.json new file mode 100644 index 0000000..e2ad4cd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/build/forgejo/runtime-base-config.json @@ -0,0 +1 @@ +{"architecture":"amd64","config":{"User":"1000:1000","ExposedPorts":{"2222/tcp":{},"3000/tcp":{}},"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GITEA_WORK_DIR=/var/lib/gitea","GITEA_CUSTOM=/var/lib/gitea/custom","GITEA_TEMP=/tmp/gitea","TMPDIR=/tmp/gitea","GITEA_APP_INI_LEGACY=/etc/gitea/app.ini","GITEA_APP_INI=/var/lib/gitea/custom/conf/app.ini","HOME=/var/lib/gitea/git"],"Entrypoint":["/usr/bin/dumb-init","--","/usr/local/bin/docker-entrypoint.sh"],"Volumes":{"/etc/gitea":{},"/var/lib/gitea":{}},"WorkingDir":"/var/lib/gitea","Labels":{"maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job.","org.opencontainers.image.documentation":"https://forgejo.org/download/#container-image","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://codeberg.org/forgejo/forgejo","org.opencontainers.image.title":"Forgejo. Beyond coding. We forge.","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"11.0.3"},"ArgsEscaped":true},"created":"2025-07-10T04:12:21.034425519Z","history":[{"created":"2025-02-14T03:28:36Z","created_by":"ADD alpine-minirootfs-3.21.3-x86_64.tar.gz / # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-02-14T03:28:36Z","created_by":"CMD [\"/bin/sh\"]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:08:22.463872647Z","created_by":"ARG RELEASE_VERSION=11.0.3","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:08:22.463872647Z","created_by":"LABEL maintainer=contact@forgejo.org org.opencontainers.image.authors=Forgejo org.opencontainers.image.url=https://forgejo.org org.opencontainers.image.documentation=https://forgejo.org/download/#container-image org.opencontainers.image.source=https://codeberg.org/forgejo/forgejo org.opencontainers.image.version=11.0.3 org.opencontainers.image.vendor=Forgejo org.opencontainers.image.licenses=GPL-3.0-or-later org.opencontainers.image.title=Forgejo. Beyond coding. We forge. org.opencontainers.image.description=Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job.","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:08:22.463872647Z","created_by":"EXPOSE map[2222/tcp:{} 3000/tcp:{}]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:08:22.463872647Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c apk --no-cache add bash ca-certificates dumb-init gettext git curl gnupg openssh-client \u0026\u0026 rm -rf /var/cache/apk/* # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:08:22.524887902Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c addgroup -S -g 1000 git \u0026\u0026 adduser -S -H -D -h /var/lib/gitea/git -s /bin/bash -u 1000 -G git git # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:08:22.622516492Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c mkdir -p /var/lib/gitea /etc/gitea # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:08:22.719074007Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c chown git:git /var/lib/gitea /etc/gitea # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:20.677652029Z","created_by":"COPY /tmp/local / # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:20.772570625Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c cd /usr/local/bin ; ln -s gitea forgejo # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:20.904591549Z","created_by":"COPY --chown=root:root /go/src/forgejo.org/gitea /app/gitea/gitea # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:20.992814487Z","created_by":"RUN |1 RELEASE_VERSION=11.0.3 /bin/sh -c ln -s /app/gitea/gitea /app/gitea/forgejo-cli # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:21.019203454Z","created_by":"COPY --chown=root:root /go/src/forgejo.org/environment-to-ini /usr/local/bin/environment-to-ini # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"COPY /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh # buildkit","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"USER 1000:1000","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV GITEA_WORK_DIR=/var/lib/gitea","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV GITEA_CUSTOM=/var/lib/gitea/custom","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV GITEA_TEMP=/tmp/gitea","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV TMPDIR=/tmp/gitea","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV GITEA_APP_INI_LEGACY=/etc/gitea/app.ini","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV GITEA_APP_INI=/var/lib/gitea/custom/conf/app.ini","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"ENV HOME=/var/lib/gitea/git","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.027356213Z","created_by":"VOLUME [/var/lib/gitea /etc/gitea]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.034425519Z","created_by":"WORKDIR /var/lib/gitea","comment":"buildkit.dockerfile.v0"},{"created":"2025-07-10T04:12:21.034425519Z","created_by":"ENTRYPOINT [\"/usr/bin/dumb-init\" \"--\" \"/usr/local/bin/docker-entrypoint.sh\"]","comment":"buildkit.dockerfile.v0","empty_layer":true},{"created":"2025-07-10T04:12:21.034425519Z","created_by":"CMD []","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350","sha256:cf760eaa10b0319495870aab29b322acf75ba0755f51f1f1a23bcf636243025b","sha256:c6d4eb3ac8d434116be911d6c5dbfbbf600b5f8db34d8564f42f916516bc8bee","sha256:4bbca535f88127e38de4d207fc606ff1f09612b9b45f4a583b39781d2a45ff91","sha256:4d7e7dd4baa79b36260ede93d34c7d57e7370d714e129c374aec60371f751977","sha256:b422e6485954feb40fe6d57beb145775bca592f0fe068477845a7e843e8d8214","sha256:51a112d24ca275b0dd383e1a9c1f725c0967f56aff9ba393d87db1c208369873","sha256:3129f4769ae273912802802ef504d87add6e9181916fb9e74ee609375c474789","sha256:cc72664d7cdf5bcebdac72c859627611fd05733067b566481d180a398b30d16b","sha256:fa9096e760abba41eb596e3d47f4bb9e9a032e906deb4ffccafa07aa6619ca7b","sha256:2c5411c383707f038d6f60e17502f73fb92254dce8d8b748f432287d12b9a633","sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"]}} \ No newline at end of file diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/tools/forgejo-release-build.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/tools/forgejo-release-build.py new file mode 100644 index 0000000..491f710 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/release-source/tools/forgejo-release-build.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +"""Build-only public Forgejo owner recipe; no deployment or publication code.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import tarfile +import time +import math +import datetime + +OWNER=Path(__file__).resolve().parents[1] +INPUTS=OWNER/'build/forgejo' +LOCK=json.loads((INPUTS/'inputs.lock.json').read_text()) + +def digest(path): + h=hashlib.sha256() + with Path(path).open('rb') as f: + for block in iter(lambda:f.read(1024*1024),b''):h.update(block) + return h.hexdigest() + +def save(path,data): + Path(path).write_text(json.dumps(data,indent=2,sort_keys=True)+'\n') + +def check(path,expected): + if digest(path)!=expected:raise ValueError('frozen public input digest differs: '+str(path)) + +def base_env(work): + return {'PATH':str(work/'node-v22.15.0-linux-x64/bin')+':'+str(work/'go/bin')+':/usr/bin:/bin', + 'HOME':str(work/'home'),'GOTOOLCHAIN':'local','GOPROXY':'off','GOSUMDB':'off','GOVCS':'*:off', + 'GOPATH':str(work/'gopath'),'GOMODCACHE':json.loads((work/'prepared.json').read_text())['module_cache'], + 'GOCACHE':str(work/'gocache'),'CGO_ENABLED':'1','GOMAXPROCS':'4', + 'GOFLAGS':'-trimpath -buildvcs=false -p=4','SOURCE_DATE_EPOCH':str(LOCK['source_date_epoch']), + 'TZ':'UTC','LC_ALL':'C','NODE_OPTIONS':'--max-old-space-size=4096', + 'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':'/dev/null','GIT_TERMINAL_PROMPT':'0', + 'NPM_CONFIG_USERCONFIG':str(work/'empty.npmrc'),'NPM_CONFIG_GLOBALCONFIG':str(work/'empty-global.npmrc'), + 'NPM_CONFIG_CACHE':str(work/'npm-cache'),'NPM_CONFIG_REGISTRY':'https://registry.npmjs.org', + 'NPM_CONFIG_OFFLINE':'true','NPM_CONFIG_AUDIT':'false','NPM_CONFIG_FUND':'false', + 'NPM_CONFIG_UPDATE_NOTIFIER':'false','PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD':'1'} + +def sandbox(work,env): + cache=Path(env['GOMODCACHE']) + return ['bwrap','--unshare-net','--unshare-pid','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib', + '--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev', + '--tmpfs','/tmp','--bind',str(work),str(work),'--ro-bind',str(cache),str(cache), + '--chdir',str(work/'source'),'--','env','-i']+[k+'='+v for k,v in env.items()] + +def run(work,name,args,env=None,offline=False,cwd=None): + actual=sandbox(work,env)+args if offline else args + start=time.time() + with (work/(name+'.log')).open('w') as out: + proc=subprocess.Popen(actual,cwd=cwd or work,env={"PATH":"/usr/bin:/bin","HOME":str(work/"home")} if offline else env,stdout=out,stderr=subprocess.STDOUT) + save(work/'process.json',{'phase':name,'pid':proc.pid,'driver_pid':os.getpid(),'argv':args,'state':'running'}) + code=proc.wait() + result={'phase':name,'argv':args,'returncode':code,'seconds':round(time.time()-start,2)} + save(work/(name+'-result.json'),result);save(work/'process.json',dict(result,state='complete',pid=None,driver_pid=None)) + print(json.dumps(result),flush=True) + if code:raise RuntimeError('phase failed; retained log: '+str(work/(name+'.log'))) + return result + +def checked_inputs(work): + for n,h in LOCK['host_tools'].items():check(n,h) + for n,h in LOCK['lockfiles'].items():check(work/'source'/n,h) + for x in LOCK['reviewed_outputs']:check(work/'source'/x['path'],x['sha256']) + +def prepare(a,work): + if work.exists():raise ValueError('workspace must be new; existing trial is never restarted or overwritten') + for n,key in [('source_archive','source_archive_sha256'),('go_archive','go_archive_sha256'),('node_archive','node_archive_sha256')]: + if not getattr(a,n):raise ValueError('explicit '+n+' required') + check(getattr(a,n),LOCK[key]) + if not a.module_cache:raise ValueError('explicit public module cache required') + cache=Path(a.module_cache).resolve();assert cache.is_dir() + check(INPUTS/LOCK['patch_file'],LOCK['patch_sha256']) + for n,h in LOCK['host_tools'].items():check(n,h) + work.mkdir(parents=True);(work/'home').mkdir() + with tarfile.open(a.source_archive) as t: + # The official archive is digest-bound before any member is extracted. + parent=work/'unpacked';parent.mkdir();t.extractall(parent,filter='data') + folders=list(parent.iterdir());assert len(folders)==1 and folders[0].is_dir();folders[0].rename(work/'source');parent.rmdir() + for n in ['go_archive','node_archive']: + with tarfile.open(getattr(a,n)) as t:t.extractall(work,filter='data') + for n in ['empty.npmrc','empty-global.npmrc']:(work/n).write_text('') + if a.go_cache_seed:shutil.copytree(Path(a.go_cache_seed).resolve(),work/'gocache') + if a.npm_cache_seed:shutil.copytree(Path(a.npm_cache_seed).resolve(),work/'npm-cache') + subprocess.run(['/usr/bin/git','apply','--check',str(INPUTS/LOCK['patch_file'])],cwd=work/'source',check=True,env={'PATH':'/usr/bin:/bin','HOME':str(work/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':'/dev/null'}) + subprocess.run(['/usr/bin/git','apply',str(INPUTS/LOCK['patch_file'])],cwd=work/'source',check=True,env={'PATH':'/usr/bin:/bin','HOME':str(work/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':'/dev/null'}) + save(work/'prepared.json',{'module_cache':str(cache),'lock_sha256':digest(INPUTS/'inputs.lock.json'),'public_archives_verified':True}) + checked_inputs(work) + packages=json.loads((work/'source/package-lock.json').read_text())['packages'] + for name,package in packages.items(): + if not name:continue + if not package.get('resolved','').startswith('https://registry.npmjs.org/') or not package.get('integrity','').startswith('sha512-'): + raise ValueError('non-public/non-integrity-bound npm package: '+name) + print(json.dumps({'prepared':True,'lockfile_npm_packages':len(packages)-1})) + +def normalize(folder): + epoch=LOCK['source_date_epoch'] + for root,dirs,files in os.walk(folder): + dirs[:]=[x for x in dirs if x not in ('node_modules','.git')] + for n in files: + p=Path(root)/n + if not p.is_symlink():os.utime(p,(epoch,epoch)) + os.utime(root,(epoch,epoch)) + +def build(work): + checked_inputs(work);env=base_env(work) + run(work,'verify-go-modules',['go','mod','verify'],env,True) + run(work,'npm-offline-ci',['npm','ci','--offline','--ignore-scripts','--no-audit','--no-fund'],env,True) + normalize(work/'source') + version=['GOFLAGS=-trimpath -buildvcs=false -p=4','RELEASE_VERSION='+LOCK['release_version'],'FORGEJO_VERSION='+LOCK['release_version'],'FORGEJO_VERSION_API='+LOCK['release_version']] + run(work,'frontend',['make']+version+['frontend'],env,True) + for n in ['public','options','templates','modules/migration/schemas']:normalize(work/'source'/n) + run(work,'embedded-and-static-backend',['make']+version+['TAGS='+LOCK['tags'],'go-check','generate-backend','static-executable'],env,True) + checked_inputs(work) + save(work/'build.json',{'binary_sha256':digest(work/'source/gitea'),'source_locks_preserved':True,'frontend_built':True,'tags':LOCK['tags'],'image_deployment_selected':False}) + +def verify(work): + checked_inputs(work);binary=work/'source/gitea' + target=work/'embedded-extracted' + if target.exists():raise ValueError('retain prior verification; extraction target already exists') + target.mkdir() + # No source, asset tree, host /usr, libraries, configuration or HOME is + # exposed to the executable. A filesystem fallback cannot pass this proof. + bare=['bwrap','--unshare-net','--unshare-pid','--proc','/proc','--dev','/dev', + '--tmpfs','/tmp','--dir','/app','--ro-bind',str(binary),'/app/forgejo', + '--bind',str(target),'/results','--chdir','/tmp','--clearenv'] + for k,v in {'PATH':'/nonexistent','HOME':'/tmp/home','USER':'builder','USERNAME':'builder', + 'GITEA_WORK_DIR':'/tmp/work','GITEA_CUSTOM':'/tmp/custom', + 'GITEA_APP_INI':'/tmp/absent-app.ini','TZ':'UTC','LC_ALL':'C'}.items(): + bare+=['--setenv',k,v] + bare+=['--','/app/forgejo'] + outer={'PATH':'/usr/bin:/bin','HOME':str(work/'home')} + run(work,'binary-version',bare+['--version'],outer) + run(work,'new-cli-help',bare+['admin','auth','add-oauth','--help'],outer) + assert '--required-claim-values' in (work/'new-cli-help.log').read_text() + assert '--secret-file' in (work/'new-cli-help.log').read_text() + run(work,'embedded-extract',bare+['embedded','extract','--include-vendored','--destination','/results','**'],outer) + records={} + for folder in ['public','options','templates']: + for f in sorted((work/'source'/folder).rglob('*')): + if not f.is_file():continue + n=str(f.relative_to(work/'source'));q=target/n + if not q.is_file() or digest(f)!=digest(q):raise ValueError('embedded asset missing/different: '+n) + records[n]=digest(f) + assert 'public/assets/js/index.js' in records and 'public/assets/css/index.css' in records + assert 'public/assets/licenses.txt' in records + save(work/'embedded-files.json',records) + readelf=subprocess.run(['/usr/bin/readelf','-l',str(binary)],capture_output=True,text=True,check=True,env={'PATH':'/usr/bin:/bin'}) + assert 'INTERP' not in readelf.stdout + save(work/'verification.json',{'binary_sha256':digest(binary),'embedded_files':len(records), + 'embedded_file_manifest_sha256':digest(work/'embedded-files.json'),'all_public_options_templates_exact':True, + 'no_source_assets_libraries_or_host_config_mounted':True, + 'no_dynamic_interpreter':True,'new_cli_flag_present':True,'network':'bwrap --unshare-net', + 'source_locks_preserved':True,'architecture_approval':False,'native_admission':False}) + print((work/'verification.json').read_text()) + +def container(work): + verified=json.loads((work/'verification.json').read_text());check(work/'source/gitea',verified['binary_sha256']) + context=work/'container-context' + if context.exists():raise ValueError('container trial already exists; retain evidence before an explicit revised trial') + archive_path=work/'image.oci.tar' + if archive_path.exists():raise ValueError('retain existing OCI artifact; never overwrite it') + base_bytes=(INPUTS/'runtime-base-config.json').read_bytes() + assert hashlib.sha256(base_bytes).hexdigest()==LOCK['runtime_base_config_sha256'] + original=json.loads(base_bytes) + epoch=max(LOCK['source_date_epoch'],math.ceil(datetime.datetime.fromisoformat(original['created'].replace('Z','+00:00')).timestamp())) + context.mkdir();shutil.copyfile(work/'source/gitea',context/'gitea') + os.utime(context/'gitea',(LOCK['source_date_epoch'],)*2) + (context/'Dockerfile').write_text('FROM '+LOCK['runtime_base']+'\nARG SOURCE_DATE_EPOCH\nLABEL org.opencontainers.image.version="'+LOCK['release_version']+'" org.opencontainers.image.revision="'+LOCK['source_commit']+'" org.railiance.source.patch.sha256="'+LOCK['patch_sha256']+'"\nCOPY --chown=0:0 --chmod=755 gitea /app/gitea/gitea\n') + os.utime(context/'Dockerfile',(LOCK['source_date_epoch'],)*2) + config=work/'docker-config';config.mkdir(exist_ok=True) + env={'PATH':'/usr/bin:/bin','HOME':str(work/'home'),'DOCKER_CONFIG':str(config),'DOCKER_HOST':'unix:///var/run/docker.sock'} + # SOURCE_DATE_EPOCH alone does not normalise COPY parent-directory mtimes. + # OCI output permits layer rewriting without the Docker-store unpack conflict. + # The epoch never predates the immutable base, preserving its exact layer prefix. + run(work,'container-build',['docker','buildx','build','--output','type=oci,dest='+str(archive_path)+',rewrite-timestamp=true', + '--no-cache','--platform',LOCK['platform'],'--pull=false','--network=none','--provenance=false','--sbom=false', + '--metadata-file',str(work/'image-build-metadata.json'),'--build-arg','SOURCE_DATE_EPOCH='+str(epoch),str(context)],env) + metadata=json.loads((work/'image-build-metadata.json').read_text()) + with tarfile.open(archive_path) as archive: + index_bytes=archive.extractfile('index.json').read();index=json.loads(index_bytes) + assert len(index['manifests'])==1 + descriptor=index['manifests'][0] + assert descriptor['platform']=={'architecture':'amd64','os':'linux'} + assert descriptor['digest']==metadata['containerimage.digest'] + def blob(d): + value=archive.extractfile('blobs/sha256/'+d.split(':')[1]).read() + assert 'sha256:'+hashlib.sha256(value).hexdigest()==d + return value + manifest=json.loads(blob(descriptor['digest'])) + config_digest=manifest['config']['digest'];config=json.loads(blob(config_digest)) + assert config_digest==metadata['containerimage.config.digest'] + for layer in manifest['layers']:blob(layer['digest']) + assert config['rootfs']['diff_ids'][:-1]==original['rootfs']['diff_ids'] + assert config['architecture']=='amd64' and config['os']=='linux' + for key in ['User','Entrypoint','Cmd','Env','WorkingDir','Volumes']: + assert config['config'].get(key)==original['config'].get(key),key + labels=dict(original['config'].get('Labels',{})) + labels.update({'org.opencontainers.image.version':LOCK['release_version'], + 'org.opencontainers.image.revision':LOCK['source_commit'],'org.railiance.source.patch.sha256':LOCK['patch_sha256']}) + assert config['config'].get('Labels')==labels + assert config['config']['User']=='1000:1000' + expected_created=datetime.datetime.fromtimestamp(epoch,datetime.timezone.utc).isoformat(timespec='seconds').replace('+00:00','Z') + assert config['created']==expected_created + found=False + last='blobs/sha256/'+manifest['layers'][-1]['digest'].split(':')[1] + with tarfile.open(fileobj=archive.extractfile(last),mode='r|*') as layer: + for member in layer: + name=member.name.lstrip('./');assert name in ('app','app/gitea','app/gitea/gitea') + if name=='app/gitea/gitea': + h=hashlib.sha256();stream=layer.extractfile(member) + for block in iter(lambda:stream.read(1024*1024),b''):h.update(block) + assert h.hexdigest()==verified['binary_sha256'];found=True + assert found + save(work/'container-result.json',{'image_manifest_digest':descriptor['digest'],'config_digest':config_digest, + 'oci_index_sha256':hashlib.sha256(index_bytes).hexdigest(),'archive_sha256':digest(archive_path), + 'artifact_path':str(archive_path),'source_epoch':LOCK['source_date_epoch'],'container_epoch':epoch, + 'all_base_layers_and_rootless_runtime_config_preserved':True,'all_oci_blob_digests_verified':True, + 'final_layer_binary_exact':True,'registry_publication':False,'deployment_tag':None, + 'host_or_secret_mounts':False,'privileged_containers':False,'local_runtime_image_loaded':False}) + print((work/'container-result.json').read_text()) + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('phase',choices=['prepare','acquire-npm','build','verify','container']) + parser.add_argument('--workspace',required=True) + for n in ['source-archive','go-archive','node-archive','module-cache','go-cache-seed','npm-cache-seed']:parser.add_argument('--'+n) + a=parser.parse_args();work=Path(a.workspace).resolve() + if not str(work).startswith('/tmp/'):raise ValueError('isolated build workspace must be under /tmp') + if a.phase=='prepare':return prepare(a,work) + assert json.loads((work/'prepared.json').read_text())['lock_sha256']==digest(INPUTS/'inputs.lock.json') + if a.phase=='acquire-npm': + checked_inputs(work);env=base_env(work);env['NPM_CONFIG_OFFLINE']='false' + return run(work,'npm-public-acquire',['npm','ci','--ignore-scripts','--no-audit','--no-fund'],env,cwd=work/'source') + {'build':build,'verify':verify,'container':container}[a.phase](work) + +if __name__=='__main__':main() diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/run_postgres.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/run_postgres.py new file mode 100644 index 0000000..0f95311 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/run_postgres.py @@ -0,0 +1,163 @@ +"""Compile and exercise the actual composed Forgejo packages on isolated PG16.""" +from pathlib import Path +import argparse +import hashlib +import json +import shutil +import subprocess +import time + +HERE = Path(__file__).resolve().parent +CLAIMS = HERE.parent / 'forgejo-keycape-claims-r1' +parser = argparse.ArgumentParser() +parser.add_argument('action', choices=['build', 'suite', 'db-regressions', 'combined-regressions', 'prepare', 'crash', 'recover-prepared', 'recover-applied', 'recover-inverse']) +parser.add_argument('--workspace', type=Path, required=True) +parser.add_argument('--attempt', type=int, default=1) +parser.add_argument('--runtime-attempt', type=int, default=2) +parser.add_argument('--control', choices=['none', 'omit-postgres-lock', 'omit-private-context'], default='none') +parser.add_argument('--source-workspace', type=Path) +parser.add_argument('--module-cache', type=Path) +parser.add_argument('--go-cache', type=Path) +args = parser.parse_args() +work = args.workspace.resolve() +assert work.is_relative_to('/tmp') +runtime_suffix = '' if args.runtime_attempt == 1 else '-attempt' + str(args.runtime_attempt) +runtime = json.loads((HERE / ('database-runtime' + runtime_suffix + '.json')).read_text()) +runtime_work = Path('/tmp/custodian-forgejo-postgres-r1' + runtime_suffix) +env = dict(PATH='/usr/bin:/bin', HOME=str(runtime_work / 'home'), + DOCKER_CONFIG=str(runtime_work / 'docker-config'), DOCKER_HOST='unix:///var/run/docker.sock') +metadata = subprocess.check_output(['docker', 'inspect', '--format', '{{json .}}', runtime['container_id']], env=env) +item = json.loads(metadata) +assert item['State']['Running'] and item['Image'] == runtime['image_id'] +assert item['Config']['Labels']['custodian.source-test'] == runtime['trial'] +assert item['HostConfig']['NetworkMode'] == 'none' and not item['HostConfig']['Privileged'] +assert not item['HostConfig']['PortBindings'] + +base = json.loads((CLAIMS / 'consumer-test-attempt1.json').read_text()) +if args.source_workspace: + source_work = args.source_workspace.resolve() + assert source_work.is_relative_to('/tmp') and source_work.is_dir() + assert args.module_cache and args.go_cache + for cache in [args.module_cache, args.go_cache]: + assert cache.resolve().is_relative_to('/tmp') and cache.is_dir() + source = source_work / 'source' + manifest = json.loads((HERE / 'composed-source-inventory.json').read_text()) +else: + source = Path(base['workspace']) / 'source' + manifest = json.loads((CLAIMS / 'composed-source-inventory.json').read_text()) +for row in manifest['files']: + assert hashlib.sha256((source / row['path']).read_bytes()).hexdigest() == row['after_sha256'], row['path'] +if args.action == 'build': + assert not work.exists(), 'new compilation workspace required; retain prior trials' + work.mkdir() + shutil.copytree(source / 'models/auth', work / 'auth') + for name in ['source_operation_postgres_test.go', 'source_operation_postgres_export_test.go']: + shutil.copyfile(HERE / name, work / 'auth' / name) + if (HERE / 'consumer-source/models/db').is_dir(): + shutil.copytree(source / 'models/db', work / 'db') + for path in (HERE / 'consumer-source/models/db').glob('*.go'): + shutil.copyfile(path, work / 'db' / path.name) + control = {'mutation': args.control} + if args.control != 'none': + file = work / 'auth/source_operation.go' + original = file.read_text() + if args.control == 'omit-private-context': + before = 'db.WithTx(db.WithSensitiveSQL(ctx), func(tx context.Context) error {' + after = 'db.WithTx(ctx, func(tx context.Context) error {' + else: + before = '''\t\t\tif _, err := db.GetEngine(tx).Exec(`LOCK TABLE login_source_operation, login_source, "user", external_login_user IN EXCLUSIVE MODE`); err != nil { +\t\t\t\treturn err +\t\t\t}''' + after = '\t\t\t// Controlled test mutation: omit the PostgreSQL table lock.' + assert original.count(before) == 1 + modified = original.replace(before, after) + file.write_text(modified) + control.update(original_source_sha256='sha256:' + hashlib.sha256(original.encode()).hexdigest(), + controlled_source_sha256='sha256:' + hashlib.sha256(modified.encode()).hexdigest(), + production_source_changed=False) + (work / 'controlled-mutation.json').write_text(json.dumps(control, indent=2) + '\n') + pins = {p.name: 'sha256:' + hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((work / 'auth').glob('*.go'))} + (work / 'source-pins.json').write_text(json.dumps(pins, indent=2) + '\n') +else: + assert (work / 'models-auth.test').is_file() + pins = json.loads((work / 'source-pins.json').read_text()) + for name, value in pins.items(): + assert 'sha256:' + hashlib.sha256((work / 'auth' / name).read_bytes()).hexdigest() == value +control = json.loads((work / 'controlled-mutation.json').read_text()) if (work / 'controlled-mutation.json').exists() else {'mutation': 'none'} +assert control['mutation'] == 'none' or args.action in ['build', 'suite'] +db_pins = {p.name: 'sha256:' + hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((work / 'db').glob('*.go'))} + +if args.source_workspace: + argv = ['bwrap', '--unshare-net', '--unshare-pid', '--die-with-parent', '--new-session'] + for name in ['/usr', '/lib', '/lib64']: + argv += ['--ro-bind', name, name] + argv += ['--symlink', 'usr/bin', '/bin', '--proc', '/proc', '--dev', '/dev', '--tmpfs', '/tmp', + '--bind', str(source_work), '/work', '--ro-bind', str(args.module_cache.resolve()), '/work/modules', + '--bind', str(args.go_cache.resolve()), '/work/gocache', '--ro-bind', '/etc/ssl/certs', '/etc/ssl/certs', + '--chdir', '/work/source', '--clearenv'] + for key, value in dict(PATH='/work/go/bin:/usr/bin:/bin', HOME='/work/home', GOPATH='/work/gopath', + GOTOOLCHAIN='local', GOPROXY='off', GOSUMDB='off', GOVCS='*:off', + GOMODCACHE='/work/modules', GOCACHE='/work/gocache', GOMAXPROCS='4', CGO_ENABLED='1', + GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL='/work/home/test.gitconfig', + GIT_TERMINAL_PROMPT='0').items(): + argv += ['--setenv', key, value] +else: + argv = base['argv'][:base['argv'].index('--')] +argv += ['--ro-bind', str(work / 'auth'), '/work/source/models/auth', + '--bind', str(work), '/pg-work', '--dir', '/run', + '--ro-bind', str(runtime_work / 'socket'), '/run/source-operation-postgres', + '--setenv', 'CUSTODIAN_PG_SOCKET', '/run/source-operation-postgres'] +if (work / 'db').is_dir(): + argv += ['--ro-bind', str(work / 'db'), '/work/source/models/db'] +if args.action == 'combined-regressions': + # The real replacement test deliberately consumes a leaf. Each run needs + # its own synthetic read-only mount and writable replacement alias. + fixtures = work / ('descriptor-fixtures-attempt' + str(args.attempt)) + assert not fixtures.exists() + fixtures.mkdir() + for name, value in [('race', 'A' * 64), ('race-replacement', 'B' * 64)]: + file = fixtures / name + file.write_text(value) + file.chmod(0o400) + argv += ['--dir', '/run', '--ro-bind', str(fixtures), '/run/forgejo-oidc', + '--bind', str(fixtures), '/run/writable-input'] +if args.action == 'build': + command = ['go', 'test', '-c', '-p', '4', '-tags', 'sqlite sqlite_unlock_notify source_operation_postgres', + '-o', '/pg-work/models-auth.test', './models/auth'] +elif args.action == 'db-regressions': + command = ['go', 'test', '-p', '1', '-json', '-tags', 'sqlite sqlite_unlock_notify', + './models/db', '-run', 'TestSensitiveSQL|TestTracingContext', '-count=1'] +elif args.action == 'combined-regressions': + command = ['go', 'test', '-p', '1', '-json', '-tags', 'sqlite sqlite_unlock_notify', + './services/auth/source/oauth2', './cmd', './models/auth', './models/db', '-run', + 'TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQL|TestTracingContext', '-count=1'] +else: + if args.action == 'suite': + selector = '^TestSourceOperationPostgres$' + else: + argv += ['--setenv', 'CUSTODIAN_PG_STEP', args.action] + selector = '^TestSourceOperationPostgresProcessRecovery$' + binary_args = ['/pg-work/models-auth.test', '-test.v', '-test.run=' + selector, '-test.count=1'] + command = binary_args if args.action == 'crash' else ['go', 'tool', 'test2json', '-t', '-p', 'forgejo.org/models/auth', *binary_args] +argv += ['--', *command] +stem = 'postgres-' + args.action + '-attempt' + str(args.attempt) +receipt = HERE / (stem + '.json') +assert not receipt.exists() +start = time.monotonic() +with (HERE / (stem + '.log')).open('xb') as output: + result = subprocess.run(argv, stdout=output, stderr=subprocess.STDOUT, timeout=600) +expected = 73 if args.action == 'crash' else (1 if args.action == 'suite' and control['mutation'] != 'none' else 0) +data = dict(action=args.action, returncode=result.returncode, expected_returncode=expected, + outcome_matches=result.returncode == expected, seconds=round(time.monotonic() - start, 2), + argv=argv, workspace=str(work), source_pins=pins, + database_source_pins=db_pins, + original_composite_patch='sha256:' + manifest['patch_sha256'], + runtime=runtime['trial'], image_id=runtime['image_id'], + controlled_mutation=control, + network='disabled; only the exact disposable database Unix socket is bound', + production_database=False, actual_authenticated_operation=False, real_credentials=False) +if (work / 'models-auth.test').is_file(): + data['test_binary_sha256'] = 'sha256:' + hashlib.sha256((work / 'models-auth.test').read_bytes()).hexdigest() +receipt.write_text(json.dumps(data, indent=2) + '\n') +print(json.dumps({k: data[k] for k in ['action', 'returncode', 'expected_returncode', 'outcome_matches', 'seconds']})) +raise SystemExit(0 if data['outcome_matches'] else 1) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/runtime-setup-refinements.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/runtime-setup-refinements.json new file mode 100644 index 0000000..b131594 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/runtime-setup-refinements.json @@ -0,0 +1,24 @@ +{ + "original_attempts_preserved": [ + "database-runtime.json", + "database-initial-failure.json", + "database-initial-failure.log", + "database-cleanup.json", + "database-runtime-attempt2.json", + "database-attempt2-failure.json", + "database-attempt2-failure.log", + "database-cleanup-attempt2.json" + ], + "observed_cause": "Image initialization intentionally clears PGHOST and PGHOSTADDR for its own SQL; a custom-only socket prevents creation of the requested synthetic database. pg_isready can accept during that temporary initialization server.", + "public_entrypoint_sha256": "sha256:9c440299ae04a0a79d55b8bf03307036d890a40979d2fb698073c9050d4b20a5", + "public_entrypoint_source": "/usr/local/bin/docker-entrypoint.sh in the pinned public database image", + "source_line": 212, + "refinements": [ + "Require final postgres PID 1 and an actual version query in the exact database", + "Preserve the default in-container socket alongside the one exported synthetic socket", + "Use fresh independently identified containers and volumes for failed initialization refinements, after exact owned cleanup" + ], + "application_package_tests_in_failed_setup_attempts": 0, + "production_changes": false, + "real_credentials": false +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source-equivalence.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source-equivalence.json new file mode 100644 index 0000000..f1012ec --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source-equivalence.json @@ -0,0 +1,8 @@ +{ + "postgres_test_binary_identical_across_final_fixture_correction": true, + "postgres_test_binary_sha256": "sha256:ea124dc75fa8c4d4063fdd2df57e506c1cd53e3c4478597e2d51b5c214de7569", + "all_database_runtime_source_files_identical": true, + "only_trace_regression_test_fixture_API_call_corrected": "Engine.AddHook takes one hook per call; preserve the earlier compile refusal.", + "normal_postgres_receipt": "postgres-suite-attempt4.json", + "sqlite_diagnostics_receipt": "postgres-db-regressions-attempt2.json" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_export_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_export_test.go new file mode 100644 index 0000000..deca965 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_export_test.go @@ -0,0 +1,7 @@ +//go:build source_operation_postgres + +package auth + +// Test-only seam: the production transaction body still obtains its real locks +// and private query context. No runtime API or authority callback is added. +var SourceOperationPostgresTransactionForTest = sourceOperationTx diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_test.go b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_test.go new file mode 100644 index 0000000..b8b75b6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/source_operation_postgres_test.go @@ -0,0 +1,369 @@ +//go:build source_operation_postgres + +package auth_test + +import ( + "bytes" + "context" + "errors" + "fmt" + "os" + "runtime/trace" + "strings" + "sync" + "testing" + "time" + + auth "forgejo.org/models/auth" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + oauth2 "forgejo.org/services/auth/source/oauth2" + + "github.com/lib/pq" + "github.com/stretchr/testify/require" + "xorm.io/xorm" +) + +type postgresOperationLog struct { + log.Logger + mu sync.Mutex + entries []string +} + +func (l *postgresOperationLog) Log(_ int, _ log.Level, format string, values ...any) { + l.mu.Lock() + defer l.mu.Unlock() + l.entries = append(l.entries, fmt.Sprintf(format, values...)) +} +func (l *postgresOperationLog) take() string { + l.mu.Lock() + defer l.mu.Unlock() + result := strings.Join(l.entries, "\n") + l.entries = nil + return result +} + +// The real Forgejo engine/driver, registered model schemas and public operation +// functions run. Only the database, application configuration and actors are +// synthetic. No deployed owner database or authenticated operation is claimed. +func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) { + t.Helper() + require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required") + oldSettings, oldContext := setting.Database, db.DefaultContext + oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine) + require.True(t, ok) + expectedSchema := schema + if expectedSchema == "" { + expectedSchema = "public" + } + raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+expectedSchema) + require.NoError(t, err) + _, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + expectedSchema + `"`) + require.NoError(t, err) + setting.Database.Type = "postgres" + setting.Database.Host = "/run/source-operation-postgres" + setting.Database.Name = "custodian_source" + setting.Database.User = "custodian_test" + setting.Database.Passwd = "" + setting.Database.Schema = schema + setting.Database.SSLMode = "disable" + setting.Database.LogSQL = false + setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2 + setting.Database.SlowQueryThreshold = 0 + require.NoError(t, db.InitEngine(context.Background())) + engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine) + require.True(t, ok) + t.Cleanup(func() { + _ = engine.Close() + _ = raw.Close() + setting.Database = oldSettings + db.SetDefaultEngine(oldContext, oldEngine) + }) + version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema") + require.NoError(t, err) + require.Equal(t, "16", string(version[0]["major"])) + require.Equal(t, expectedSchema, string(version[0]["schema"])) + require.NoError(t, db.SyncAllTables()) + auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source)) + return db.DefaultContext, engine, raw +} + +func resetPostgresOperations(t *testing.T, engine *xorm.Engine) { + t.Helper() + _, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`) + require.NoError(t, err) + _, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`) + require.NoError(t, err) +} + +func TestSourceOperationPostgres(t *testing.T) { + for _, profile := range []struct{ name, schema string }{{"default", ""}, {"public", "public"}, {"named", "custodian_named"}} { + t.Run(profile.name, func(t *testing.T) { + ctx, engine, raw := postgresOperationDB(t, profile.schema) + run := func(name string, f func(*testing.T)) { + t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) }) + } + run("create-reconcile-inverse", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource()) + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + require.Positive(t, r.SourceID) + post := operationRows(t, ctx) + again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, r, again) + require.Equal(t, post, operationRows(t, ctx)) + _, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + require.Equal(t, before, operationRows(t, ctx)) + _, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + }) + run("exact-raw-preexisting-restoration", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new" + _, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + _, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + run("unchanged-preexisting-survives", func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, "unchanged", r.Phase) + _, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case") + require.NoError(t, err) + require.Equal(t, before, operationRows(t, ctx)) + }) + for _, stage := range []string{"before-apply", "before-inverse"} { + run("private-drift-"+stage, func(t *testing.T) { + s := operationSource() + require.NoError(t, auth.CreateSource(ctx, s)) + _, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s) + require.NoError(t, err) + if stage == "before-inverse" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + require.NoError(t, err) + } + _, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID) + require.NoError(t, err) + before := operationRows(t, ctx) + if stage == "before-apply" { + _, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } else { + _, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case") + } + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + }) + } + for _, kind := range []string{"user", "external"} { + run("linked-"+kind+"-refuses-deletion", func(t *testing.T) { + _, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource()) + require.NoError(t, err) + r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.NoError(t, err) + if kind == "user" { + _, err = engine.Insert(&user_model.User{ID: 1, LowerName: "synthetic", Name: "synthetic", Email: "synthetic@invalid.test", LoginSource: r.SourceID}) + } else { + _, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID) + } + require.NoError(t, err) + before := operationRows(t, ctx) + _, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case") + require.True(t, auth.IsErrSourceInUse(err)) + require.Equal(t, before, operationRows(t, ctx)) + var count int64 + if kind == "user" { + count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count() + } else { + count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count() + } + require.NoError(t, err) + require.EqualValues(t, 1, count) + }) + } + run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) { + before := operationRows(t, ctx) + _, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource()) + require.NoError(t, err) + _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`) + require.NoError(t, err) + _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`) + require.NoError(t, err) + _, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.ErrorIs(t, err, auth.ErrSourceOperationConflict) + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + }) + run("driver-error-rollback-and-private-diagnostics", func(t *testing.T) { + captured := new(postgresOperationLog) + engine.AddHook(&db.SlowQueryHook{Logger: captured}) + engine.AddHook(&db.ErrorQueryHook{Logger: captured}) + const control = "synthetic-postgres-unmarked-control" + _, err := db.GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", control) + require.Error(t, err) + require.Contains(t, captured.take(), control, "actual unmarked diagnostic control must be observable") + _, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_fail_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '%', NEW.cfg; END $$`) + require.NoError(t, err) + _, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_fail_source()`) + require.NoError(t, err) + before := operationRows(t, ctx) + _, err = auth.PrepareSourceOperation(ctx, "postgres-error-operation", "postgres-error-case", 0, operationSource()) + require.NoError(t, err) + captured.take() + var tracing bytes.Buffer + require.NoError(t, trace.Start(&tracing)) + _, err = auth.ApplySourceOperation(ctx, "postgres-error-operation", "postgres-error-case") + trace.Stop() + require.ErrorIs(t, err, auth.ErrSourceOperationStorage) + messages := captured.take() + require.Contains(t, messages, "[Error SQL Query] [owner-confined query]") + require.Contains(t, messages, "[Slow SQL Query] [owner-confined query]") + require.NotContains(t, messages, "synthetic-private-original") + require.False(t, bytes.Contains(tracing.Bytes(), []byte("synthetic-private-original")), "actual PostgreSQL trace disclosed private cfg") + require.Equal(t, before, operationRows(t, ctx)) + r, err := auth.ReconcileSourceOperation(ctx, "postgres-error-operation", "postgres-error-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + }) + run("uncooperative-writers-excluded", func(t *testing.T) { + locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1) + var once sync.Once + releaseLock := func() { once.Do(func() { close(release) }) } + defer releaseLock() + go func() { + done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil }) + }() + select { + case <-locked: + case err := <-done: + t.Fatalf("lock acquisition refused: %v", err) + case <-time.After(5 * time.Second): + t.Fatal("lock acquisition timed out") + } + statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`} + for _, statement := range statements { + session := raw.NewSession() + require.NoError(t, session.Begin()) + _, err := session.Exec(`SET LOCAL lock_timeout='150ms'`) + require.NoError(t, err) + _, err = session.Exec(statement) + var pgerr *pq.Error + blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03" + _ = session.Rollback() + _ = session.Close() + require.True(t, blocked, "an uncooperative writer escaped the actual table lock") + } + releaseLock() + select { + case err := <-done: + require.NoError(t, err) + case <-time.After(5 * time.Second): + t.Fatal("transaction did not finish") + } + for _, statement := range statements { + _, err := raw.Exec(statement) + require.NoError(t, err) + } + }) + run("concurrent-preparation-has-one-owner", func(t *testing.T) { + start, done := make(chan struct{}), make(chan error, 2) + for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} { + go func(id string) { + <-start + _, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource()) + done <- err + }(id) + } + close(start) + ok, conflict := 0, 0 + for range 2 { + select { + case err := <-done: + if err == nil { + ok++ + } else if errors.Is(err, auth.ErrSourceOperationConflict) { + conflict++ + } else { + t.Fatal("unexpected concurrent preparation error") + } + case <-time.After(5 * time.Second): + t.Fatal("concurrent preparation timed out") + } + } + require.Equal(t, 1, ok) + require.Equal(t, 1, conflict) + }) + }) + } +} + +func TestSourceOperationPostgresProcessRecovery(t *testing.T) { + step := os.Getenv("CUSTODIAN_PG_STEP") + require.NotEmpty(t, step, "explicit synthetic recovery phase required") + ctx, engine, _ := postgresOperationDB(t, "custodian_recovery") + switch step { + case "prepare": + resetPostgresOperations(t, engine) + _, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource()) + require.NoError(t, err) + case "crash": + engine.AddHook(operationCrashAfterSourceInsert{}) + _, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + t.Fatal("actual source insert did not reach the process-death hook") + case "recover-prepared": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "prepared", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + _, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-applied": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "applied", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.EqualValues(t, 1, n) + _, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + case "recover-inverse": + r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case") + require.NoError(t, err) + require.Equal(t, "reverted", r.Phase) + n, err := engine.Table("login_source").Count() + require.NoError(t, err) + require.Zero(t, n) + default: + t.Fatal("unknown synthetic recovery phase") + } +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/test_release_cli.py b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/test_release_cli.py new file mode 100644 index 0000000..323c682 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/test_release_cli.py @@ -0,0 +1,107 @@ +import pathlib,subprocess,json,sqlite3,os,shlex,time +import argparse,shutil +parser=argparse.ArgumentParser(description='Actual static-release CLI against a new synthetic SQLite workspace') +parser.add_argument('--workspace',type=pathlib.Path,required=True) +parser.add_argument('--binary',type=pathlib.Path,required=True) +args=parser.parse_args() +w=args.workspace.resolve() +assert w.is_relative_to('/tmp') and not w.exists() +w.mkdir();(w/'home').mkdir() +shutil.copyfile(args.binary,w/'forgejo');(w/'forgejo').chmod(0o755) +fixtures=w/'fixtures'; fixtures.mkdir(exist_ok=True) +A='AbCdEf012345678-'*4;B='ZyXwVu_987654321'*4 # synthetic exact64 +assert len(A)==64 and len(B)==64 +for name,data,mode in [('good',A,0o400),('replacement',B,0o440),('newline',A+'\n',0o400),('short',A[:-1],0o400),('long',A+'A',0o400),('badbyte',A[:-1]+'=',0o400),('world',A,0o444),('writable',A,0o600),('executable',A,0o500),('empty','',0o400),('CLIENT_SECRET','',0o400)]: + p=fixtures/name + if p.exists():p.chmod(0o600) + p.write_text(data);p.chmod(mode) +for name in ['symlink']: + p=fixtures/name + if not p.exists():p.symlink_to('good') +if not (fixtures/'fifo').exists():os.mkfifo(fixtures/'fifo',0o400) +(fixtures/'directory').mkdir(exist_ok=True) +(fixtures/'badparent').mkdir(exist_ok=True);(fixtures/'badparent').chmod(0o777) +if (fixtures/'badparent/value').exists():(fixtures/'badparent/value').chmod(0o600) +(fixtures/'badparent/value').write_text(A);(fixtures/'badparent/value').chmod(0o400) +if not (fixtures/'parentlink').exists():(fixtures/'parentlink').symlink_to('directory',target_is_directory=True) +(w/'runtime').mkdir(exist_ok=True) +dbfile=w/'runtime/test.db' +if dbfile.exists():dbfile.unlink() # isolated synthetic test database only +ini=w/'runtime/app.ini';ini.write_text(f'''APP_NAME = synthetic-private-input-test +RUN_USER = worsch +WORK_PATH = {w}/runtime +[security] +INSTALL_LOCK = true +SECRET_KEY = SYNTHETIC-TEST-KEY +[database] +DB_TYPE = sqlite3 +SLOW_QUERY_THRESHOLD = 1ns +PATH = {w}/runtime/test.db +[server] +APP_DATA_PATH = {w}/runtime/data +OFFLINE_MODE = true +[repository] +ROOT = {w}/runtime/repos +[log] +MODE = console +LEVEL = Error +''') +env={'PATH':'/usr/bin:/bin','HOME':str(w/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':str(w/'home/test.gitconfig'),'GIT_TERMINAL_PROMPT':'0','USER':'worsch','GITEA_WORK_DIR':str(w/'runtime')} +base=['bwrap','--unshare-pid','--unshare-net','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib','--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev','--tmpfs','/tmp','--bind',str(w),str(w),'--dir','/run','--ro-bind',str(fixtures),'/run/forgejo-oidc','--ro-bind',str(fixtures/'good'),'/run/forgejo-oidc/CLIENT_SECRET','--bind',str(fixtures),'/run/writable-input','--chdir',str(w/'runtime'),'--','env','-i']+[k+'='+v for k,v in env.items()]+[str(w/'forgejo'),'--config',str(ini)] +results=[] +def run(args,ok=True,reason=None): + p=subprocess.run(base+args,capture_output=True,text=True,timeout=60); output=p.stdout+p.stderr + assert A not in output and B not in output, 'synthetic input leaked in output' + assert all(A not in arg and B not in arg for arg in base+args), 'synthetic input in argv' + assert all(A not in v and B not in v for v in env.values()), 'synthetic input in env' + results.append({'operation':args,'returncode':p.returncode,'stdout_stderr_secret_absent':True}) + (w/'cli-attempts.json').write_text(json.dumps(results,indent=2)) + if (p.returncode==0)!=ok:raise AssertionError('CLI unexpected status: '+repr(args)+'\n'+output[-5000:]) + if reason:assert reason in output,output[-2000:] + return p +run(['migrate']) +clean_ini=ini.read_text() +ini.write_text(clean_ini.replace('DB_TYPE = sqlite3', 'DB_TYPE = sqlite3\nLOG_SQL = true')) +run(['admin','auth','add-oauth','--name','refused-debug-logging','--provider','github','--secret-file','/run/forgejo-oidc/CLIENT_SECRET'],False,'OAuth configuration requires SQL logging disabled') +ini.write_text(clean_ini) +def rows(): + with sqlite3.connect(w/'runtime/test.db') as db:return db.execute('select id,name,cfg from login_source order by id').fetchall() +def cfg():return json.loads(rows()[0][2]) +run(['admin','auth','add-oauth','--name','synthetic-oauth','--provider','github','--key','synthetic-public-id','--secret-file','/run/forgejo-oidc/CLIENT_SECRET','--group-claim-name','groups','--admin-group','unchanged-admin','--scopes','read:user,user:email']) +assert len(rows())==1 and cfg()['ClientSecret']==A +id=str(rows()[0][0]); before=cfg() +run(['admin','auth','update-oauth','--id',id,'--icon-url','https://synthetic.invalid/icon']) +assert cfg()['ClientSecret']==A +preserved=cfg() +run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement']) +after=cfg();assert after['ClientSecret']==B +assert {k:v for k,v in after.items() if k!='ClientSecret'}=={k:v for k,v in preserved.items() if k!='ClientSecret'},'secret replacement changed unrelated config' +# Guarded inactive source exercises actual update omission and strict list semantics +# without discovery/network or claiming a browser login. +with sqlite3.connect(w/'runtime/test.db') as db: + guarded=after|{'Provider':'openidConnect','OpenIDConnectAutoDiscoveryURL':'https://synthetic.invalid/.well-known/openid-configuration','RequiredClaimName':'groups','RequiredClaimValues':['forge-admins','forge-coulomb-owners','forge-coulomb-readers']} + db.execute('update login_source set is_active=0,cfg=? where id=?',(json.dumps(guarded),id)) +run(['admin','auth','update-oauth','--id',id,'--name','synthetic-guarded']) +assert cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] and cfg()['ClientSecret']==B +run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/good']) +assert cfg()['ClientSecret']==A and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] +for path in ['/run/forgejo-oidc/'+n for n in ['newline','short','long','badbyte','world','writable','executable','empty','symlink','fifo','directory','missing','badparent/value','parentlink/value']]+['relative','','/run/forgejo-oidc/../forgejo-oidc/good','/run/writable-input/good']: + before=rows() + for cmd in [['admin','auth','add-oauth','--name','refused','--provider','github'],['admin','auth','update-oauth','--id',id,'--name','refused']]: + run(cmd+['--secret-file',path],False,'secret-file input refused') + assert rows()==before,'failure changed DB' +for action in [['add-oauth','--name','refused'],['update-oauth','--id',id]]: + before=rows();run(['admin','auth']+action+['--secret','','--secret-file','/run/forgejo-oidc/good'],False,'mutually exclusive');assert rows()==before +# Failure to clear/ambiguously replace gate keeps stored application bytes unchanged. +before=rows();run(['admin','auth','update-oauth','--id',id,'--required-claim-values',''],False);assert rows()==before +# Actual XORM error hook under a failing SQLite trigger must not disclose cfg. +before=rows() +with sqlite3.connect(w/'runtime/test.db') as database: + database.execute("create trigger synthetic_private_error before update on login_source begin select raise(abort, 'synthetic-db-failure'); end") +run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'],False,'OAuth source update failed') +assert rows()==before +with sqlite3.connect(w/'runtime/test.db') as database:database.execute('drop trigger synthetic_private_error') +# Legacy explicit secret remains supported; synthetic legacy string is not private input. +run(['admin','auth','update-oauth','--id',id,'--secret','synthetic-legacy-literal']) +assert cfg()['ClientSecret']=='synthetic-legacy-literal' and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] +(w/'cli-results.json').write_text(json.dumps({'passed':True,'subprocesses':len(results),'real_backend_cli_and_sqlite':True,'network':'disabled by bwrap','secret_inputs':'synthetic only','private_file_values_never_in_argv_env_output':True,'actual_browser_login':False,'actual_readonly_file_bind':True,'replacement_preserves_other_config':True,'unsafe_SQL_logging_refused':True,'actual_error_and_slow_hooks_no_leak':True},indent=2));print('CLI integrations passed:',len(results)) diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/tracing-context-correction.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/tracing-context-correction.json new file mode 100644 index 0000000..f0c2164 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/tracing-context-correction.json @@ -0,0 +1,11 @@ +{ + "observed_failure": "postgres-suite-attempt3.log", + "runtime_preimage_sha256": "sha256:43683804d0c5bc6ead1dba0344c69dd196825cf3d5eb502af8d932a7b8207b96", + "runtime_postimage_sha256": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8", + "cause": "The actual XORM BeforeProcess aggregator does not propagate one hook return into the ContextHook passed to later hooks. A later normal hook returns the original context, losing the tracing task; AfterProcess panics.", + "correction": "Publish the new task context into the shared ContextHook before returning it. Preserve existing query/private markers and pair the same task in AfterProcess.", + "production_changes": false, + "original_frozen_source_and_release_unchanged": true, + "xorm_version": "1.3.9", + "xorm_hook_source_sha256": "sha256:49a766675cefe5eedd36bd8682a3d0cb117ac68c6faacdaac7a78b2acdad9612" +} diff --git a/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/validation.json b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/validation.json new file mode 100644 index 0000000..8044e92 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/validation.json @@ -0,0 +1,41 @@ +{ + "postgres-suite-attempt4": { + "passed_including_subtests": 37, + "skips": 0, + "returncode": 0 + }, + "postgres-db-regressions-attempt2": { + "passed_including_subtests": 6, + "skips": 0, + "returncode": 0 + }, + "postgres-combined-regressions-attempt2": { + "passed_including_subtests": 94, + "skips": 0, + "returncode": 0 + }, + "client_crash_after_actual_insert": true, + "actual_database_server_restart": true, + "fresh_process_reconciliation_and_inverse": true, + "controlled_counterexamples": "controlled-counterexamples.json", + "runtime_source_correction": "tracing-context-correction.json", + "production_database": false, + "real_credentials": false, + "native_gates_closed": 0, + "complete_release_rebuild": { + "frontend_and_static_backend": "passed", + "release_version": "11.0.3+cust80.feb10793", + "binary_sha256": "sha256:92efb26614d3fca22c400920d92c4cd022e32ab86c1f562f60699ab5f750cd28", + "embedded_assets_exact": 2234, + "actual_release_CLI_checks": 48, + "exact_local_OCI_exports": 2, + "manifest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7", + "native_admission": false + }, + "server_version_num": 160014, + "database_restart_receipt": "database-restart-attempt3.json", + "exact_owned_runtime_cleanup": "database-cleanup-attempt3.json", + "runtime_removed": true, + "owner_source_commit": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3", + "owner_projection": "primary/railiance01" +} diff --git a/docs/netkingdom-target-implementation.md b/docs/netkingdom-target-implementation.md index cb1ce14..a77f92d 100644 --- a/docs/netkingdom-target-implementation.md +++ b/docs/netkingdom-target-implementation.md @@ -1353,3 +1353,32 @@ A passing synthetic callback is not an actual issuer or factor acceptance receip resource/operation/inverse coverage, source/package re-freeze, custody and native admission remain existing owner work. All sixteen native gates remain open. +### PostgreSQL source mechanics and tracing-context correction + +Apps `308e83fc` publishes the next 40-path build candidate. The actual Forgejo +engine, registered schemas and OAuth configuration types run on disposable +PostgreSQL 16.14 with default, explicit-public and named schemas. All 37 +test/subtest results pass, including exact restoration, drift/link refusal, +trigger rollback, raw writer exclusion and concurrent preparation. Separate +clients recover after process death following the real INSERT; a committed apply +survives an actual database restart before fresh reconciliation and inverse. + +An actual diagnostics check exposed a tracing-hook composition panic when a +later hook discarded the task context. Publishing it into XORM's shared query +context preserves tracing and the private-query marker. Six SQLite DB checks +and all 94 selected composed results pass without skips. Controlled source +copies without the PostgreSQL lock or private marker fail their corresponding +tests in all three schema profiles. Original setup, fixture, compilation and +runtime failures remain retained. Every owned database container/volume is removed. + +The complete revised frontend/static release builds; all 2,234 embedded assets +are exact and all 48 actual release CLI/SQLite checks pass. Two fresh no-cache +local OCI exports reproduce identical archive, manifest, config and index while +preserving base layers and rootless settings. One source build is claimed. +The source and receipts are in `forgejo-postgres-source-r1` under 18 October. + +This is source/database mechanics proof. Existing operation/inverse session and +evidence authority, release-wide exclusion, private reader/backup custody and +retention, ongoing HTTP/API/Git/SSH paths, independent owner review and deployed +database/package/runtime acceptance remain open. No native target gate closes. + diff --git a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md index 743bc3c..5ccb226 100644 --- a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md +++ b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md @@ -78,6 +78,33 @@ bootstrap callers and approved exception evidence. Reuse owner work where it already covers the finding. Record new concrete uncovered implementation work before closing this task. Never populate secret values or invent owner approval. +2026-10-18 PostgreSQL continuation: the actual Forgejo engine/model schemas and +OAuth configuration types now pass 37 test/subtest results on disposable +PostgreSQL 16.14 across default, explicit-public and named schemas. Separate +client processes verify rollback after death immediately following INSERT, +committed apply survival through an actual database-server restart, and fresh +reconciliation/inverse. Exact restoration, drift/link refusals, trigger rollback, +uncooperative writer exclusion and concurrent preparation pass. Controlled +copies without the table lock or private-query marker fail in all three profiles. + +The diagnostics test exposed a real XORM tracing-context composition panic. +Publishing the task context into the shared query hook fixes the actual path +without changing the incoming private marker. Six SQLite diagnostic checks and +all 94 selected composed package results pass without skips. Earlier container, +fixture and compilation failures, the original panic and both counterexamples +remain retained. All owned test containers and volumes were removed. + +Apps `308e83fc` publishes the 40-path source/build correction at the primary hub +with all nine task UUIDs/statuses preserved. The complete fresh frontend/static +release, all 2,234 embedded assets and 48 release CLI/SQLite checks pass. Two +fresh local no-cache OCI exports reproduce the same archive/manifest/config/index; +no second source build, registry publication or deployed acceptance is claimed. +Evidence: `docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1/`. +Current operation/inverse identities, every release writer, private journal +custody/retention, ongoing resource paths and independent/native acceptance remain +under existing T34/T42/T43 and RAPPS16-T07/T08/T09. This source proof closes no +native target gate; the complete goal and ledger scope remain active. + 2026-10-18 goal continuation: implemented the missing producer-owned Go client, generated export parity check, validated Forgejo policy fixtures/Rego tests and actual consumer callback coverage. Access Engine source `ca0cb757` is committed