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
81 changes: 15 additions & 66 deletions .github/workflows/evaos-live-canary-proof.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ jobs:
AIONUI_EVAOS_MAC_CONTROL_CANARY_SUPABASE_URL: ${{ secrets.AIONUI_EVAOS_MAC_CONTROL_CANARY_SUPABASE_URL }}
AIONUI_EVAOS_MAC_CONTROL_CANARY_SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.AIONUI_EVAOS_MAC_CONTROL_CANARY_SUPABASE_SERVICE_ROLE_KEY }}
AIONUI_EVAOS_MAC_CONTROL_CANARY_TTL_MINUTES: '10'
AIONUI_EVAOS_MAC_CONTROL_DEPLOYED_PROBE_OUTPUT: live-canary-proof/mac-control-deployed-route.json
AIONUI_EVAOS_MAC_CONTROL_DEPLOYED_NEGATIVE_PROBE_OUTPUT: live-canary-proof/mac-control-runtime-negative.json
EVAOS_LIVE_CANARY_CONTEXT_KEY_ID: ${{ vars.EVAOS_MAC_CONTROL_CONTEXT_KEY_ID }}
EVAOS_LIVE_CANARY_RECEIPT_KEY_ID: ${{ vars.EVAOS_MAC_CONTROL_RECEIPT_KEY_ID }}
EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY: ${{ vars.EVAOS_MAC_CONTROL_RECEIPT_PUBLIC_KEY }}
steps:
- name: Validate proof inputs
env:
Expand Down Expand Up @@ -255,75 +260,19 @@ jobs:
set -euo pipefail
node scripts/evaosProvisionLiveCanaryFixtures.js cleanup-mac-control > "$PROOF_DIR/mac-control-session-cleanup.stdout.json"

- name: Run Mac-control proof secret/redaction scan
id: mac-control-proof-scan
- name: Scan existing Mac-control proofs for safe diagnostic upload
id: mac-control-partial-proof-scan
if: always() && github.event.inputs.run_mac_control_canary == 'true'
run: |
Comment thread
100yenadmin marked this conversation as resolved.
set -euo pipefail
node - "$PROOF_DIR" <<'NODE'
const fs = require('fs');
const path = require('path');
node scripts/evaosScanMacControlProofs.js --allow-partial "$PROOF_DIR"

const proofDir = process.argv[2];
const proofNames = [
'mac-control-runtime.json',
'mac-control-session-provisioning.json',
'mac-control-session-provisioning.stdout.json',
'mac-control-session-cleanup.json',
'mac-control-session-cleanup.stdout.json',
];
const forbiddenFields = new Set([
'accountEmail',
'bindingId',
'customerId',
'customer_id',
'desktopSession',
'endpoint',
'launchUrl',
'session',
]);
const forbiddenValue = /\beds_[A-Za-z0-9_-]{8,}\b|\bBearer\s+|\beyJ[A-Za-z0-9_-]{8,}\.|https?:\/\/|[?&]session=|\S+@\S+/i;

function scan(value, location) {
if (typeof value === 'string' && forbiddenValue.test(value)) {
throw new Error(`Mac-control proof contains forbidden sensitive material at ${location}.`);
}
if (Array.isArray(value)) {
value.forEach((entry, index) => scan(entry, `${location}[${index}]`));
return;
}
if (!value || typeof value !== 'object') return;
for (const [key, entry] of Object.entries(value)) {
if (forbiddenFields.has(key)) {
throw new Error(`Mac-control proof contains forbidden field ${key}.`);
}
scan(entry, `${location}.${key}`);
}
}

let scanned = 0;
for (const proofName of proofNames) {
const proofPath = path.join(proofDir, proofName);
if (!fs.existsSync(proofPath)) continue;
scan(JSON.parse(fs.readFileSync(proofPath, 'utf8')), proofName);
scanned += 1;
}
if (scanned === 0) {
throw new Error('Mac-control secret/redaction scan found no proof artifacts.');
}
const cleanupPath = path.join(proofDir, 'mac-control-session-cleanup.json');
if (!fs.existsSync(cleanupPath)) {
throw new Error('Mac-control proof is missing sanitized cleanup evidence.');
}
const cleanupProof = JSON.parse(fs.readFileSync(cleanupPath, 'utf8'));
if (
cleanupProof.schema !== 'evaos-mac-control-canary-session-cleanup/v1' ||
cleanupProof.sessionRevoked !== true ||
cleanupProof.sensitiveOutput !== 'passed'
) {
throw new Error('Mac-control proof did not prove temporary session revocation.');
}
NODE
- name: Require complete Mac-control proof set
id: mac-control-complete-proof-scan
if: success() && github.event.inputs.run_live_canaries == 'true' && github.event.inputs.run_mac_control_canary == 'true' && github.event.inputs.provision_fixtures == 'true' && steps.mac-control-partial-proof-scan.outcome == 'success'
run: |
set -euo pipefail
node scripts/evaosScanMacControlProofs.js "$PROOF_DIR"

- name: Write summary
if: always()
Expand All @@ -346,7 +295,7 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"

- name: Upload live canary proof packet
if: always() && (github.event.inputs.run_mac_control_canary != 'true' || steps.mac-control-proof-scan.outcome == 'success')
if: always() && (github.event.inputs.run_mac_control_canary != 'true' || steps.mac-control-partial-proof-scan.outcome == 'success')
uses: actions/upload-artifact@v4
with:
name: evaos-live-canary-proof-${{ github.run_id }}
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,39 @@ jobs:
- name: Run extension system tests
run: bunx vitest run

openclaw-plugin-contract:
name: OpenClaw Plugin Contract
if: github.event_name == 'workflow_dispatch' || (github.event.action != 'closed' && github.event.pull_request.draft == false)
Comment thread
100yenadmin marked this conversation as resolved.
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Resolve PR context
uses: ./.github/actions/checkout-pr
with:
pr_number: ${{ inputs.pr_number }}
github_token: ${{ github.token }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: resources/evaos-beta/bridge/agent-tools/openclaw-plugin/package-lock.json

- name: Install locked plugin dependencies
working-directory: resources/evaos-beta/bridge/agent-tools/openclaw-plugin
run: npm ci --ignore-scripts --omit=peer --no-audit --no-fund

- name: Run complete plugin contract suite
working-directory: resources/evaos-beta/bridge/agent-tools/openclaw-plugin
run: npm test

- name: Reject generated plugin drift
Comment thread
100yenadmin marked this conversation as resolved.
Comment thread
100yenadmin marked this conversation as resolved.
run: git diff --exit-code -- resources/evaos-beta/bridge/agent-tools/openclaw-plugin/dist

Comment thread
coderabbitai[bot] marked this conversation as resolved.
windows-unit-tests:
name: Unit Tests (windows-2022)
needs: pr-check-plan
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release-distribute.yml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ jobs:
EVAOS_LIVE_CANARY_MAX_PROOF_AGE_HOURS: '24'
EVAOS_LIVE_CANARY_EXPECTED_SOURCE_HEAD_SHA: ${{ steps.provenance.outputs.tag_commit }}
EVAOS_LIVE_CANARY_EXPECTED_SOURCE_RUN_ID: ${{ github.event.inputs.live_canary_proof_run_id }}
EVAOS_LIVE_CANARY_CONTEXT_KEY_ID: ${{ vars.EVAOS_MAC_CONTROL_CONTEXT_KEY_ID }}
EVAOS_LIVE_CANARY_RECEIPT_KEY_ID: ${{ vars.EVAOS_MAC_CONTROL_RECEIPT_KEY_ID }}
EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY: ${{ vars.EVAOS_MAC_CONTROL_RECEIPT_PUBLIC_KEY }}
run: |
/bin/bash scripts/evaosValidateLiveCanaryProofRun.sh

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/workbench-functional-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ jobs:
BRIDGE="$APP_PATH/Contents/Resources/Bridge/evaos-desktop-bridge"
BRIDGE_PEEKABOO="$APP_PATH/Contents/Resources/Bridge/bin/peekaboo"
BRIDGE_HELPER="$APP_PATH/Contents/Resources/Bridge/bin/evaos-connector-helper"
BRIDGE_RECEIPT_VERIFIER="$APP_PATH/Contents/Resources/Bridge/bin/evaos-ed25519-verify"
BRIDGE_PEEKABOO_LICENSE="$APP_PATH/Contents/Resources/Bridge/licenses/Peekaboo-LICENSE.txt"
BRIDGE_MANIFEST="$APP_PATH/Contents/Resources/Bridge/manifest.json"
BRIDGE_PYTHON="$APP_PATH/Contents/Resources/Bridge/python/bin/python3"
Expand All @@ -209,6 +210,7 @@ jobs:
test -x "$BRIDGE"
test -x "$BRIDGE_PEEKABOO"
test -x "$BRIDGE_HELPER"
test -x "$BRIDGE_RECEIPT_VERIFIER"
test -f "$BRIDGE_PEEKABOO_LICENSE"
test -f "$BRIDGE_MANIFEST"
test -x "$BRIDGE_PYTHON"
Expand Down Expand Up @@ -245,6 +247,8 @@ jobs:
;;
esac
codesign --verify --strict "$BRIDGE_PEEKABOO"
codesign --verify --strict "$BRIDGE_RECEIPT_VERIFIER"
test "$(lipo -archs "$BRIDGE_RECEIPT_VERIFIER")" = arm64
Comment thread
100yenadmin marked this conversation as resolved.
if [ "$BUNDLED_PEEKABOO_SOURCE_SHA256" != "$PEEKABOO_BINARY_SHA256" ]; then
echo "::error::Bundled Peekaboo source digest does not match the pinned release binary."
exit 1
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jspm_packages/

# Lock files for other package managers (project uses bun.lock)
package-lock.json
!resources/evaos-beta/bridge/agent-tools/openclaw-plugin/package-lock.json
yarn.lock

# TypeScript v1 declaration files
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
bounded deadline so a healthy Mac is not misreported as unauthenticated.
- Binds the packaged bridge source digest and ownership provenance to the exact
evaOS-GUI release commit before signing.
- Treats Tailscale `NeedsLogin` as an installed, running client that still
needs enrollment, immediately shows progress for the single allowed
`Connect this Mac` action, keeps the renderer IPC alive for the bounded
enrollment window, and surfaces a strictly sanitized rejection instead of
leaving the button apparently inert. Duplicate enrollment clicks remain
disabled while the first action is in flight.
- Verifies genuine Mac App Store Tailscale with Apple's App Store signing
requirement while retaining the exact Tailscale team and bundle-identifier
checks; standalone Tailscale remains pinned to its Apple-anchored Developer
ID team requirement.
- Makes the Hermes adapter parse only the two connector assignments instead of
executing an environment file, carries JSON parameters over standard input,
and rejects caller-selected local payload paths in the OpenClaw fallback.

### Selected-Binding Mac-control Canary

Expand All @@ -47,6 +60,22 @@
compensates failed persistence by revoking the exact temporary session.
- Requires matching normalized capability sets, a secure host-only browser
session cookie, and a still-live selected binding after callback completion.
- Cross-binds the connector's private SSHSIG receipt to a separately signed,
minimal public runtime attestation. OpenClaw verifies both signatures and
returns the public envelope unchanged; the live canary and distribution gate
independently verify it against protected external key configuration and
reject legacy unsigned, forged, replayed, stale, wrong-run, or wrong-head
evidence.
- Carries connector receipt-signer settings into the LaunchAgent only for an
explicitly selected staging canary; ordinary customer starts ignore ambient
canary variables, while incomplete explicit staging configuration still
fails before writing or starting the service.
- Adds deployed-route behavior probes for gateway authentication, POST-only
routing, exact path matching, strict body validation, and rejection of
caller-supplied authority before Mac-control proof can authorize publication.
- Requires the installed-app QA binding check to consume the signed selected-
binding attestation within a bounded one-hour evidence window, while still
proving that its short-lived authority was valid at execution time.

### Release Authorization And Bundle Integrity

Expand Down
13 changes: 10 additions & 3 deletions packages/desktop/src/common/evaos/nativeCompanionBoundary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,16 @@ const EVAOS_EXPECTED_WORKBENCH_BUILD_ARG =
const EVAOS_EXPECTED_SOURCE_COMMIT_ARG =
'"${EVAOS_WORKBENCH_EXPECTED_SOURCE_COMMIT:?Set EVAOS_WORKBENCH_EXPECTED_SOURCE_COMMIT to the exact 40-character evaOS-GUI commit}"';
const EVAOS_SELECTED_BINDING_PROOF_ARG =
'"${EVAOS_MAC_CONTROL_LIVE_CANARY_PROOF:?Set EVAOS_MAC_CONTROL_LIVE_CANARY_PROOF to the sanitized selected-binding callback proof}"';
'"${EVAOS_MAC_CONTROL_LIVE_CANARY_PROOF:?Set EVAOS_MAC_CONTROL_LIVE_CANARY_PROOF to the sanitized selected-binding runtime-receipt proof}"';
const EVAOS_SELECTED_BINDING_PROOF_RUN_ID_ARG =
'"${EVAOS_MAC_CONTROL_LIVE_CANARY_RUN_ID:?Set EVAOS_MAC_CONTROL_LIVE_CANARY_RUN_ID to the exact proof workflow run id}"';
const EVAOS_LIVE_CANARY_RECEIPT_KEY_ID_ENV =
'EVAOS_LIVE_CANARY_RECEIPT_KEY_ID="${EVAOS_LIVE_CANARY_RECEIPT_KEY_ID:?Set EVAOS_LIVE_CANARY_RECEIPT_KEY_ID to the pinned receipt verifier key id}"';
const EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY_ENV =
'EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY="${EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY:?Set EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY to the pinned receipt verifier public key}"';
const EVAOS_LIVE_CANARY_CONTEXT_KEY_ID_ENV =
'EVAOS_LIVE_CANARY_CONTEXT_KEY_ID="${EVAOS_LIVE_CANARY_CONTEXT_KEY_ID:?Set EVAOS_LIVE_CANARY_CONTEXT_KEY_ID to the pinned execution-context key id}"';
const EVAOS_SELECTED_BINDING_TRUST_ENV = `${EVAOS_LIVE_CANARY_RECEIPT_KEY_ID_ENV} ${EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY_ENV} ${EVAOS_LIVE_CANARY_CONTEXT_KEY_ID_ENV}`;

export const EVAOS_NATIVE_COMPANION_CANARIES = [
{
Expand All @@ -83,13 +90,13 @@ export const EVAOS_NATIVE_COMPANION_CANARIES = [
},
{
id: 'connector-all',
command: `${EVAOS_PACKAGED_BRIDGE_COMMAND} qa-canary --connector-url ${EVAOS_CONNECTOR_URL_ARG} --artifact-dir ${EVAOS_CANARY_ARTIFACT_DIR_ARG} --version-under-test ${EVAOS_EXPECTED_WORKBENCH_VERSION_ARG} --build-under-test ${EVAOS_EXPECTED_WORKBENCH_BUILD_ARG} --source-commit-under-test ${EVAOS_EXPECTED_SOURCE_COMMIT_ARG} --selected-binding-proof ${EVAOS_SELECTED_BINDING_PROOF_ARG} --selected-binding-proof-run-id ${EVAOS_SELECTED_BINDING_PROOF_RUN_ID_ARG} --surface connector --suite all --operator-ack-live-control`,
command: `${EVAOS_SELECTED_BINDING_TRUST_ENV} ${EVAOS_PACKAGED_BRIDGE_COMMAND} qa-canary --connector-url ${EVAOS_CONNECTOR_URL_ARG} --artifact-dir ${EVAOS_CANARY_ARTIFACT_DIR_ARG} --version-under-test ${EVAOS_EXPECTED_WORKBENCH_VERSION_ARG} --build-under-test ${EVAOS_EXPECTED_WORKBENCH_BUILD_ARG} --source-commit-under-test ${EVAOS_EXPECTED_SOURCE_COMMIT_ARG} --selected-binding-proof ${EVAOS_SELECTED_BINDING_PROOF_ARG} --selected-binding-proof-run-id ${EVAOS_SELECTED_BINDING_PROOF_RUN_ID_ARG} --surface connector --suite all --operator-ack-live-control`,
requiredArtifact: 'qa-report.json',
forbidsSkips: true,
},
{
id: 'connector-kill-switch',
command: `${EVAOS_PACKAGED_BRIDGE_COMMAND} qa-canary --connector-url ${EVAOS_CONNECTOR_URL_ARG} --artifact-dir ${EVAOS_CANARY_ARTIFACT_DIR_ARG} --version-under-test ${EVAOS_EXPECTED_WORKBENCH_VERSION_ARG} --build-under-test ${EVAOS_EXPECTED_WORKBENCH_BUILD_ARG} --source-commit-under-test ${EVAOS_EXPECTED_SOURCE_COMMIT_ARG} --selected-binding-proof ${EVAOS_SELECTED_BINDING_PROOF_ARG} --selected-binding-proof-run-id ${EVAOS_SELECTED_BINDING_PROOF_RUN_ID_ARG} --surface connector --suite kill_switch --operator-ack-live-control`,
command: `${EVAOS_SELECTED_BINDING_TRUST_ENV} ${EVAOS_PACKAGED_BRIDGE_COMMAND} qa-canary --connector-url ${EVAOS_CONNECTOR_URL_ARG} --artifact-dir ${EVAOS_CANARY_ARTIFACT_DIR_ARG} --version-under-test ${EVAOS_EXPECTED_WORKBENCH_VERSION_ARG} --build-under-test ${EVAOS_EXPECTED_WORKBENCH_BUILD_ARG} --source-commit-under-test ${EVAOS_EXPECTED_SOURCE_COMMIT_ARG} --selected-binding-proof ${EVAOS_SELECTED_BINDING_PROOF_ARG} --selected-binding-proof-run-id ${EVAOS_SELECTED_BINDING_PROOF_RUN_ID_ARG} --surface connector --suite kill_switch --operator-ack-live-control`,
requiredArtifact: 'qa-report.json',
forbidsSkips: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const SECURE_NETWORK_ENROLL_SETTLE_COMMAND_TIMEOUT_MS = 2000;
let secureNetworkEnrollmentInFlight = false;

export type EvaosNativeCompanionDiagnosticEventCode =
| 'secure_network_enrollment_provider_received'
| 'secure_network_enrollment_action_started'
| 'secure_network_enrollment_preflight_failed'
| 'secure_network_enrollment_broker_request_started'
Expand Down Expand Up @@ -2140,6 +2141,9 @@ export async function runNativeCompanionAction(
request: IEvaosNativeCompanionActionRequest,
deps: EvaosNativeCompanionStatusDeps = {}
): Promise<IEvaosNativeCompanionActionResult> {
if (request.action === 'secure_network_enroll') {
recordNativeCompanionDiagnosticEvent(deps, 'secure_network_enrollment_provider_received');
}
const existsSync = deps.existsSync ?? fs.existsSync;
const bridgePath = resolveBridgeExecutable(
deps.bridgePaths ?? defaultBridgePaths(deps.env, nativeCompanionIsPackaged(deps)),
Expand Down Expand Up @@ -2317,8 +2321,11 @@ async function verifiedSecureNetworkClient(
const clientVariant = identifier ? SECURE_NETWORK_APP_IDENTIFIERS[identifier] : undefined;
if (teamIdentifier !== SECURE_NETWORK_APP_TEAM_ID || !clientVariant) continue;
const requirement =
`anchor apple generic and certificate leaf[subject.OU] = "${SECURE_NETWORK_APP_TEAM_ID}" ` +
`and identifier "${identifier}"`;
clientVariant === 'tailscale_app_store'
? `anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists ` +
`and identifier "${identifier}"`
: `anchor apple generic and certificate leaf[subject.OU] = "${SECURE_NETWORK_APP_TEAM_ID}" ` +
`and identifier "${identifier}"`;
await execFile('/usr/bin/codesign', ['--verify', '--deep', '--strict', `-R=${requirement}`, appPath], {
timeout: COMMAND_TIMEOUT_MS,
});
Expand Down
Loading
Loading