Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/client-v1-conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,7 @@ jobs:
@('scripts\phase1-macos-keychain.mjs', 5091, 'ab0c2dd08cf606d9502f5da206175707d471d99f484e8c8c79b5b08a5772b9a4'),
@('scripts\phase1-process-supervisor.mjs', 3820, '16b51fb1a33b4bfef98daca549aacf5dc2d2c098cfbd664753b69c940d1e6f6c'),
@('scripts\phase1-schema-v2-evidence.mjs', 52505, '0aede2ab3abd76fabf5ac61d64d2dbaaffa497c8647b82236403de16a47751c8'),
@('scripts\phase1-schema-v2-producer.mjs', 233796, '1d0c28f526c3e71256693da6ef26be6fe2327cb30e820960a4cf3761c4b8c4f6'),
@('scripts\phase1-schema-v2-producer.mjs', 233914, 'ea32a0b7d43f0558521de091666d66960bc033afafd5e619a728da1ec5d45929'),
@('scripts\process-owned-artifact-root.mjs', 13061, '103cc789f12a6bbde16b2414aecf05813d9d28a2c40c7d6eaa2073b86e8e5d77'),
@('scripts\supervised-exec.mjs', 2875, 'a5edfd985b934d3b46247a0da3141682c411d30bb582edf87ae7b29791dad65b'),
@('scripts\supervisor-status.mjs', 854, 'ac332ca7b6b040ecc846088bb3a6ad5e7112a0454eb3ea71d2a819d55e64254e')
Expand Down Expand Up @@ -1910,7 +1910,7 @@ jobs:
- name: Prepare trusted Unix supervisor
if: matrix.platform != 'win32-x64'
shell: bash
run: "set -euo pipefail\nnode --input-type=module <<'EOF'\nimport { createHash } from 'node:crypto';\nimport { lstatSync, readFileSync } from 'node:fs';\nconst expected = new Map([\n ['scripts/contract-canary.mjs', [40618, 'a4c2fe0a5eb6a5ff4653de5374c34c0fb46907c6806a5d23b86d8b37206ef958']],\n ['scripts/executable-resolution.mjs', [9154, '31e3c412ff8c835f14522f36a59e91f4a4ba82913210ae8e3b4455217503f430']],\n ['scripts/owned-temp-directory.mjs', [7762, '95f546ef9ed614f2a0f55d356ddfc54c943fc53b595b4eebebfcbd4db68e5c0b']],\n ['scripts/phase1-artifact-secret-scan.mjs', [21183, 'be0ec302b9c4372f232d6bd1efcba873fd3380cc5de7f756cd0b9eeeec07222a']],\n ['scripts/phase1-conformance-lock.mjs', [48960, '92f981c43f75bc65c81e9e9ee16084aae658617b929d451a9db0d7c9e6bedbe2']],\n ['scripts/phase1-conformance.mjs', [219462, 'fd90e35308ea6b8dd0488cb874d00d7ad40aa16b33e38b46a9a76c3ea13d5056']],\n ['scripts/phase1-evidence-contract.mjs', [15088, '24180ae03835fa6aac45559682adb3c1e626bab76466eddc55b9e2300f0a2b7f']],\n ['scripts/phase1-evidence-runtime.mjs', [6572, 'b09a0d20abc7ebbc6289c57c985fd42b8a4b5d196895bc035b8fca40281d9315']],\n ['scripts/phase1-linux-secret-service.mjs', [4270, 'ddf834c6f57853c5116b4b1f345952a218ff0687c5d741737c68e20bc2ecda92']],\n ['scripts/phase1-macos-keychain.mjs', [5091, 'ab0c2dd08cf606d9502f5da206175707d471d99f484e8c8c79b5b08a5772b9a4']],\n ['scripts/phase1-process-supervisor.mjs', [3820, '16b51fb1a33b4bfef98daca549aacf5dc2d2c098cfbd664753b69c940d1e6f6c']],\n ['scripts/phase1-schema-v2-evidence.mjs', [52505, '0aede2ab3abd76fabf5ac61d64d2dbaaffa497c8647b82236403de16a47751c8']],\n ['scripts/phase1-schema-v2-producer.mjs', [233796, '1d0c28f526c3e71256693da6ef26be6fe2327cb30e820960a4cf3761c4b8c4f6']],\n ['scripts/process-owned-artifact-root.mjs', [13061, '103cc789f12a6bbde16b2414aecf05813d9d28a2c40c7d6eaa2073b86e8e5d77']],\n ['scripts/supervised-exec.mjs', [2875, 'a5edfd985b934d3b46247a0da3141682c411d30bb582edf87ae7b29791dad65b']],\n ['scripts/supervisor-status.mjs', [854, 'ac332ca7b6b040ecc846088bb3a6ad5e7112a0454eb3ea71d2a819d55e64254e']],\n ['scripts/phase1-linux-secret-service.sh', [5650, '83ce19c0dd6da5002f6853fa37addb4fc2d39f3d17beee1b1c39e1fce232b476']],\n ['scripts/unix-artifact-handoff.c', [18704, '2a003f9aa1d1886b9a593371a73cb65fe3a4a8b703f1c59fec8a27694367b7fc']],\n ['scripts/unix-producer-command.sh', [3223, 'ce9ec2ff00947f3ec0db53f144c99d34bc27de6085062d00dccff7c934c2e3c8']],\n ['scripts/unix-producer-supervisor.sh', [29424, 'b73036415744c80ed27d5667f255ceea149096ca517b47c93a154299802206ff']],\n]);\nfor (const [path, [size, digest]] of expected) {\n const stats = lstatSync(path);\n const bytes = readFileSync(path);\n if (\n !stats.isFile() ||\n stats.isSymbolicLink() ||\n stats.nlink !== 1 ||\n bytes.byteLength !== size ||\n createHash('sha256').update(bytes).digest('hex') !== digest\n ) {\n throw new Error('Trusted Unix supervisor source bytes do not match');\n }\n}\nconsole.log('Frozen harness module graph verified.');\nEOF\nbroker_root=\"/tmp/opencoven-unix-broker\"\n(umask 077 && mkdir \"$broker_root\")\ncc -std=c11 -D_DARWIN_C_SOURCE -Wall -Wextra -Werror -O2 \\\n scripts/unix-artifact-handoff.c \\\n -o \"$broker_root/unix-artifact-handoff\"\nchmod 500 \"$broker_root/unix-artifact-handoff\"\n"
run: "set -euo pipefail\nnode --input-type=module <<'EOF'\nimport { createHash } from 'node:crypto';\nimport { lstatSync, readFileSync } from 'node:fs';\nconst expected = new Map([\n ['scripts/contract-canary.mjs', [40618, 'a4c2fe0a5eb6a5ff4653de5374c34c0fb46907c6806a5d23b86d8b37206ef958']],\n ['scripts/executable-resolution.mjs', [9154, '31e3c412ff8c835f14522f36a59e91f4a4ba82913210ae8e3b4455217503f430']],\n ['scripts/owned-temp-directory.mjs', [7762, '95f546ef9ed614f2a0f55d356ddfc54c943fc53b595b4eebebfcbd4db68e5c0b']],\n ['scripts/phase1-artifact-secret-scan.mjs', [21183, 'be0ec302b9c4372f232d6bd1efcba873fd3380cc5de7f756cd0b9eeeec07222a']],\n ['scripts/phase1-conformance-lock.mjs', [48960, '92f981c43f75bc65c81e9e9ee16084aae658617b929d451a9db0d7c9e6bedbe2']],\n ['scripts/phase1-conformance.mjs', [219462, 'fd90e35308ea6b8dd0488cb874d00d7ad40aa16b33e38b46a9a76c3ea13d5056']],\n ['scripts/phase1-evidence-contract.mjs', [15088, '24180ae03835fa6aac45559682adb3c1e626bab76466eddc55b9e2300f0a2b7f']],\n ['scripts/phase1-evidence-runtime.mjs', [6572, 'b09a0d20abc7ebbc6289c57c985fd42b8a4b5d196895bc035b8fca40281d9315']],\n ['scripts/phase1-linux-secret-service.mjs', [4270, 'ddf834c6f57853c5116b4b1f345952a218ff0687c5d741737c68e20bc2ecda92']],\n ['scripts/phase1-macos-keychain.mjs', [5091, 'ab0c2dd08cf606d9502f5da206175707d471d99f484e8c8c79b5b08a5772b9a4']],\n ['scripts/phase1-process-supervisor.mjs', [3820, '16b51fb1a33b4bfef98daca549aacf5dc2d2c098cfbd664753b69c940d1e6f6c']],\n ['scripts/phase1-schema-v2-evidence.mjs', [52505, '0aede2ab3abd76fabf5ac61d64d2dbaaffa497c8647b82236403de16a47751c8']],\n ['scripts/phase1-schema-v2-producer.mjs', [233914, 'ea32a0b7d43f0558521de091666d66960bc033afafd5e619a728da1ec5d45929']],\n ['scripts/process-owned-artifact-root.mjs', [13061, '103cc789f12a6bbde16b2414aecf05813d9d28a2c40c7d6eaa2073b86e8e5d77']],\n ['scripts/supervised-exec.mjs', [2875, 'a5edfd985b934d3b46247a0da3141682c411d30bb582edf87ae7b29791dad65b']],\n ['scripts/supervisor-status.mjs', [854, 'ac332ca7b6b040ecc846088bb3a6ad5e7112a0454eb3ea71d2a819d55e64254e']],\n ['scripts/phase1-linux-secret-service.sh', [5650, '83ce19c0dd6da5002f6853fa37addb4fc2d39f3d17beee1b1c39e1fce232b476']],\n ['scripts/unix-artifact-handoff.c', [18704, '2a003f9aa1d1886b9a593371a73cb65fe3a4a8b703f1c59fec8a27694367b7fc']],\n ['scripts/unix-producer-command.sh', [3223, 'ce9ec2ff00947f3ec0db53f144c99d34bc27de6085062d00dccff7c934c2e3c8']],\n ['scripts/unix-producer-supervisor.sh', [29424, 'b73036415744c80ed27d5667f255ceea149096ca517b47c93a154299802206ff']],\n]);\nfor (const [path, [size, digest]] of expected) {\n const stats = lstatSync(path);\n const bytes = readFileSync(path);\n if (\n !stats.isFile() ||\n stats.isSymbolicLink() ||\n stats.nlink !== 1 ||\n bytes.byteLength !== size ||\n createHash('sha256').update(bytes).digest('hex') !== digest\n ) {\n throw new Error('Trusted Unix supervisor source bytes do not match');\n }\n}\nconsole.log('Frozen harness module graph verified.');\nEOF\nbroker_root=\"/tmp/opencoven-unix-broker\"\n(umask 077 && mkdir \"$broker_root\")\ncc -std=c11 -D_DARWIN_C_SOURCE -Wall -Wextra -Werror -O2 \\\n scripts/unix-artifact-handoff.c \\\n -o \"$broker_root/unix-artifact-handoff\"\nchmod 500 \"$broker_root/unix-artifact-handoff\"\n"
- name: Compute reviewed Unix tool path
id: unix-tool-path
if: matrix.platform != 'win32-x64'
Expand Down
34 changes: 30 additions & 4 deletions docs/phase1-conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ assignment, RPC decoder and primary/secondary exception pipeline. This fixture
correction does not establish the protected installation failure's cause or
relax the round-trip assertions.

The lock now selects reviewed source `b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2`,
tree `1642836d4bfaab3b54f93ef58aaabeb930af3d9e`, including all 25 governed files
The lock now selects reviewed source `aa4d33004c59202cae70e3fc5a56a70cdf451dac`,
tree `378e977dc2a3d49cc5f97d1740a1a0fdbda6ee75`, including all 25 governed files
and ten production deltas. The checkout regression exercises all five labels
from that immutable revision. SDK rebinding, both scope rotations and fresh
protected validation remain required; this binding alone is not acceptance.
Expand Down Expand Up @@ -2020,7 +2020,7 @@ revision authorities can therefore have different workflow hashes:

| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `.github/workflows/client-v1-conformance.yml` | 177,934 | `79ba921d0bb49c2e88ac5253928e44489462fc80b2ac98b53ab9ed13533fba87` |
| `.github/workflows/client-v1-conformance.yml` | 177,934 | `5bb78a9ea0a333bf010728e075721f2482afa4bb5674702551943f5dd0a1c087` |
| `scripts/contract-canary.mjs` | 40,618 | `a4c2fe0a5eb6a5ff4653de5374c34c0fb46907c6806a5d23b86d8b37206ef958` |
| `scripts/executable-resolution.mjs` | 9,154 | `31e3c412ff8c835f14522f36a59e91f4a4ba82913210ae8e3b4455217503f430` |
| `scripts/owned-temp-directory.mjs` | 7,762 | `95f546ef9ed614f2a0f55d356ddfc54c943fc53b595b4eebebfcbd4db68e5c0b` |
Expand All @@ -2033,7 +2033,7 @@ revision authorities can therefore have different workflow hashes:
| `scripts/phase1-macos-keychain.mjs` | 5,091 | `ab0c2dd08cf606d9502f5da206175707d471d99f484e8c8c79b5b08a5772b9a4` |
| `scripts/phase1-process-supervisor.mjs` | 3,820 | `16b51fb1a33b4bfef98daca549aacf5dc2d2c098cfbd664753b69c940d1e6f6c` |
| `scripts/phase1-schema-v2-evidence.mjs` | 52,505 | `0aede2ab3abd76fabf5ac61d64d2dbaaffa497c8647b82236403de16a47751c8` |
| `scripts/phase1-schema-v2-producer.mjs` | 233,796 | `1d0c28f526c3e71256693da6ef26be6fe2327cb30e820960a4cf3761c4b8c4f6` |
| `scripts/phase1-schema-v2-producer.mjs` | 233,914 | `ea32a0b7d43f0558521de091666d66960bc033afafd5e619a728da1ec5d45929` |
| `scripts/process-owned-artifact-root.mjs` | 13,061 | `103cc789f12a6bbde16b2414aecf05813d9d28a2c40c7d6eaa2073b86e8e5d77` |
| `scripts/supervised-exec.mjs` | 2,875 | `a5edfd985b934d3b46247a0da3141682c411d30bb582edf87ae7b29791dad65b` |
| `scripts/supervisor-status.mjs` | 854 | `ac332ca7b6b040ecc846088bb3a6ad5e7112a0454eb3ea71d2a819d55e64254e` |
Expand Down Expand Up @@ -2788,6 +2788,32 @@ Fresh reviewed source binding, SDK rebinding, and protected validation are
required before attributing the Windows failure or claiming a repaired run.


### Unsupported custody installation

`secure_store_unavailable` covered two unrelated causes on the installation
preflight. `KeyringError::Unavailable` is returned both by a secure store that
is genuinely unavailable and by `CredentialCustody::installation_id`'s default
trait body, which a custody implementation reaches only by never overriding it.
`InstallationStage::classify` already rewrites `Unavailable` into
`installation_lock_unavailable`, `installation_entry_unavailable`,
`installation_read_unavailable`, `installation_write_unavailable` and
`installation_persistence_unavailable`, so those five stages were already
distinguishable; the default trait body was the remaining unclassified path.

It now returns the fixed code `installation_custody_unsupported`, published as
`phase1.native-scenarios.native-preflight-installation-custody-unsupported`.
The outer launcher derives its native-stage allowlist from the producer
registry, so the identifier survives extraction without a second edit. Only the
fixed identifier is published: no message, stack, path, credential or
subprocess output is added.

Protected run
[35100084575](https://github.com/OpenCoven/chat/actions/runs/35100084575)
reported `native-preflight-installation-secure-store-unavailable` on Windows
while Linux and macOS passed. This change does not repair that failure and does
not establish its cause; it separates the two causes so the next protected run
attributes it.

### Unexpected installation RPC failures

Protected run `34964120550` used Chat `d84195c61b86598e691ccd47163e46a15b154417`
Expand Down
18 changes: 9 additions & 9 deletions phase1-conformance.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
},
"harness": {
"repository": "OpenCoven/chat",
"revision": "b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2"
"revision": "aa4d33004c59202cae70e3fc5a56a70cdf451dac"
},
"harnessAuthority": {
"revision": "b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2",
"tree": "1642836d4bfaab3b54f93ef58aaabeb930af3d9e",
"revision": "aa4d33004c59202cae70e3fc5a56a70cdf451dac",
"tree": "378e977dc2a3d49cc5f97d1740a1a0fdbda6ee75",
"files": [
{
"path": "scripts/phase1-conformance.mjs",
Expand Down Expand Up @@ -81,8 +81,8 @@
},
{
"path": "scripts/phase1-schema-v2-producer.mjs",
"blob": "942917311fbb26d60fa1d38d8becbd6193b6799d",
"sha256": "1d0c28f526c3e71256693da6ef26be6fe2327cb30e820960a4cf3761c4b8c4f6"
"blob": "720445dfaf8ceb071f966bccbc8111a350c31ed0",
"sha256": "ea32a0b7d43f0558521de091666d66960bc033afafd5e619a728da1ec5d45929"
},
{
"path": "scripts/phase1-linux-secret-service.mjs",
Expand Down Expand Up @@ -146,8 +146,8 @@
},
{
"path": ".github/workflows/client-v1-conformance.yml",
"blob": "4e693a7c780265823bd041ec9f2972006bfd00b5",
"sha256": "79ba921d0bb49c2e88ac5253928e44489462fc80b2ac98b53ab9ed13533fba87"
"blob": "d24779c90233fe94350439a13d2542a2023113b3",
"sha256": "5bb78a9ea0a333bf010728e075721f2482afa4bb5674702551943f5dd0a1c087"
}
],
"productionDeltas": [
Expand Down Expand Up @@ -193,8 +193,8 @@
},
{
"path": "src-tauri/src/keyring.rs",
"blob": "b235ab40e02265ea3e9fbaedf0a842e7fc627f4f",
"sha256": "ba9be1f65792ae988ff64ca1348fc9ec4438925e4f4822f3c2d3057eee5d0568"
"blob": "11bd10925b3e18a27fd55fccbada1d0fb67db781",
"sha256": "72e1363f2f241e435136237731f3d84ca5a4ba9dad00f784027146331dcd8abc"
},
{
"path": "src-tauri/src/lib.rs",
Expand Down
2 changes: 2 additions & 0 deletions scripts/phase1-schema-v2-producer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const nativeLaunchPublicationFailures = new WeakMap();
const nativeRpcFailureCategories = new WeakMap();
const nativeInstallationResponseCategories = new Map([
['secure_store_unavailable', 'secure-store-unavailable'],
['installation_custody_unsupported', 'custody-unsupported'],
['installation_lock_unavailable', 'lock-unavailable'],
['installation_entry_unavailable', 'entry-unavailable'],
['installation_read_unavailable', 'read-unavailable'],
Expand Down Expand Up @@ -173,6 +174,7 @@ const schemaV2NativeFailureStages = new Set([
'native-preflight-custody-proof',
'native-preflight-installation-rpc',
'native-preflight-installation-secure-store-unavailable',
'native-preflight-installation-custody-unsupported',
'native-preflight-installation-lock-unavailable',
'native-preflight-installation-entry-unavailable',
'native-preflight-installation-read-unavailable',
Expand Down
70 changes: 69 additions & 1 deletion src-tauri/src/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub(crate) enum KeyringError {
Unavailable,
#[cfg(feature = "phase1-conformance")]
InstallationUnavailable(InstallationStage),
#[cfg(feature = "phase1-conformance")]
CustodyInstallationUnsupported,
Failure,
#[cfg(feature = "phase1-conformance")]
CleanupGrantRejected,
Expand Down Expand Up @@ -162,6 +164,10 @@ impl KeyringError {
},
true,
),
#[cfg(feature = "phase1-conformance")]
Self::CustodyInstallationUnsupported => {
NativeDiagnostic::new("installation_custody_unsupported", true)
}
Self::Failure => NativeDiagnostic::new("keychain_failure", true),
#[cfg(feature = "phase1-conformance")]
Self::CleanupGrantRejected => NativeDiagnostic::new("cleanup_grant_rejected", false),
Expand Down Expand Up @@ -287,8 +293,13 @@ pub(crate) enum CredentialSlot {
}

pub(crate) trait CredentialCustody: Send + Sync {
// A custody implementation that does not override this is a distinct
// condition from an unavailable secure store; keep them separable.
fn installation_id(&self) -> Result<String, KeyringError> {
Err(KeyringError::Unavailable)
#[cfg(feature = "phase1-conformance")]
return Err(KeyringError::CustodyInstallationUnsupported);
#[cfg(not(feature = "phase1-conformance"))]
return Err(KeyringError::Unavailable);
}

fn read(&self, instance_id: &str, origin: &str) -> Result<Credential, KeyringError>;
Expand Down Expand Up @@ -2349,6 +2360,63 @@ mod tests {
));
}
}
#[cfg(feature = "phase1-conformance")]
#[test]
fn unsupported_custody_installation_is_distinct_from_an_unavailable_store() {
// A custody type that does not override installation_id must not be
// reported as an unavailable secure store; the two are separate causes.
struct BareCustody;
impl super::CredentialCustody for BareCustody {
fn read(&self, _: &str, _: &str) -> Result<super::Credential, KeyringError> {
Err(KeyringError::Failure)
}
fn read_for_pairing_update(
&self,
_: &str,
_: &str,
) -> Result<super::CredentialSlot, KeyringError> {
Err(KeyringError::Failure)
}
fn store_if_current(
&self,
_: &str,
_: &str,
_: Option<&super::Credential>,
_: &str,
_: &str,
) -> Result<bool, KeyringError> {
Err(KeyringError::Failure)
}
fn replace_stale_if_current(
&self,
_: &str,
_: &str,
_: &super::Credential,
_: &str,
_: &str,
) -> Result<bool, KeyringError> {
Err(KeyringError::Failure)
}
fn delete_if_matches(
&self,
_: &str,
_: &str,
_: &super::Credential,
) -> Result<bool, KeyringError> {
Err(KeyringError::Failure)
}
}
let error = super::CredentialCustody::installation_id(&BareCustody)
.expect_err("the default custody implementation must fail");
assert!(matches!(
error,
KeyringError::CustodyInstallationUnsupported
));
let diagnostic = error.diagnostic();
assert_eq!(diagnostic.code, "installation_custody_unsupported");
assert_ne!(diagnostic.code, KeyringError::Unavailable.diagnostic().code);
assert!(diagnostic.retryable);
}
#[cfg(unix)]
use super::{
acquire_mutation_lock_detailed_with_timeout_at, acquire_mutation_lock_with_timeout_at,
Expand Down
2 changes: 2 additions & 0 deletions src/native-preflight-diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ test.each([
'installation-rpc',
'installation-id',
'installation-secure-store-unavailable',
'installation-custody-unsupported',
'installation-lock-unavailable',
'installation-entry-unavailable',
'installation-read-unavailable',
Expand Down Expand Up @@ -134,6 +135,7 @@ class InstallationChild extends EventEmitter {

test.each([
['response', 'secure_store_unavailable', 'secure-store-unavailable'],
['response', 'installation_custody_unsupported', 'custody-unsupported'],
['response', 'installation_lock_unavailable', 'lock-unavailable'],
['response', 'installation_entry_unavailable', 'entry-unavailable'],
['response', 'installation_read_unavailable', 'read-unavailable'],
Expand Down
Loading
Loading