deadbyte-mcp 0.15.72 → 0.15.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +75 -27
- package/PROVENANCE.json +42 -14
- package/README.txt +24 -18
- package/RELEASE.json +52 -18
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +30 -2
- package/bin/deadbyte-owner-token-r193.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R184-package-lock.json +2 -2
- package/native/deadbyte-owner-token-r193.cpp +156 -0
- package/package.json +2 -2
- package/scripts/gate-windows-r193.ps1 +108 -0
- package/scripts/gate-windows-r194.ps1 +112 -0
- package/scripts/npm-interactive-r194.mjs +13 -0
- package/scripts/npm-publication-r65.mjs +64 -4
- package/scripts/npm-release-r48.mjs +58 -11
- package/scripts/npm-release-readiness-r193.mjs +80 -0
- package/scripts/npm-release-readiness-r194.mjs +80 -0
- package/scripts/r188-owner-uac-review.ps1 +91 -0
- package/scripts/r188-uac-review-preview.mjs +65 -0
- package/scripts/r193-compile-token.ps1 +22 -0
- package/scripts/r193-evidence-relocate.ps1 +12 -0
- package/scripts/r193-focused-gate.mjs +20 -0
- package/scripts/r193-import-r188-scripts.ps1 +14 -0
- package/scripts/r193-limited-subject-probe.ps1 +25 -0
- package/scripts/r193-migrate-runtime-limited.ps1 +103 -0
- package/scripts/r193-owner-keygen.mjs +43 -0
- package/scripts/r193-owner-request-live.mjs +50 -0
- package/scripts/r193-owner-uac-approve.ps1 +42 -0
- package/scripts/r193-owner-uac-broker.mjs +139 -0
- package/scripts/r193-owner-uac-enroll-and-approve.ps1 +62 -0
- package/scripts/r193-test-limited-token.ps1 +49 -0
- package/scripts/r193-toolchain-inventory.ps1 +7 -0
- package/scripts/r193-uac-bridge-focused.mjs +21 -0
- package/src/r188-owner-uac.mjs +105 -0
- package/src/r188-uac-boundary.mjs +21 -0
- package/src/r193-live-owner-subject.mjs +110 -0
- package/src/r193-owner-secret-acl.mjs +67 -0
- package/src/r193-owner-uac-broker.mjs +85 -0
- package/src/r193-uac-r70-bridge.mjs +54 -0
- package/src/version.mjs +1 -1
- package/test/process-policy-refresh.test.mjs +1 -1
- package/test/r130-release-security-contract.test.mjs +8 -8
- package/test/r132-release-identity.test.mjs +7 -7
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r160-release-contract.test.mjs +3 -3
- package/test/r165-live-cli-skew.test.mjs +1 -1
- package/test/r167-release-contract.test.mjs +11 -11
- package/test/r168-release-contract.test.mjs +4 -4
- package/test/r172-release-contract.test.mjs +6 -6
- package/test/r173-release-contract.test.mjs +16 -16
- package/test/r179-release-contract.test.mjs +11 -11
- package/test/r188-owner-uac.test.mjs +48 -0
- package/test/r188-uac-boundary.test.mjs +42 -0
- package/test/r190-release-contract.test.mjs +10 -10
- package/test/r191-release-contract.test.mjs +12 -12
- package/test/r192-release-identity.test.mjs +10 -10
- package/test/r193-limited-migration-contract.test.mjs +26 -0
- package/test/r193-live-owner-subject.test.mjs +61 -0
- package/test/r193-native-process-observer.test.mjs +36 -0
- package/test/r193-native-token.test.mjs +43 -0
- package/test/r193-owner-enrollment-entrypoint.test.mjs +38 -0
- package/test/r193-owner-secret-acl.test.mjs +23 -0
- package/test/r193-owner-uac-broker.test.mjs +82 -0
- package/test/r193-owner-uac-entrypoint.test.mjs +36 -0
- package/test/r193-release-identity.test.mjs +56 -0
- package/test/r193-release-readiness.test.mjs +87 -0
- package/test/r193-uac-r70-bridge.test.mjs +88 -0
- package/test/r193-windows-gate.test.mjs +30 -0
- package/test/r194-npm-auth-recovery.test.mjs +77 -0
- package/test/r194-npm-preauth.test.mjs +57 -0
- package/test/r194-release-identity.test.mjs +43 -0
- package/test/r194-release-readiness.test.mjs +87 -0
- package/test/r194-windows-gate.test.mjs +21 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
|
Binary file
|
package/bridge/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "deadbyte.process-policy.v1",
|
|
3
3
|
"enabled": true,
|
|
4
|
-
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
5
|
-
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
4
|
+
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.74-r194-npm-auth-recovery\\controller\\runtime\\process-grant.json",
|
|
5
|
+
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.74-r194-npm-auth-recovery\\controller\\runtime\\process-state",
|
|
6
6
|
"native_host": {
|
|
7
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
7
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.74-r194-npm-auth-recovery\\bin\\deadbyte-process-host.exe",
|
|
8
8
|
"sha256": "daf9cfb371bee88cafbf5597eaa507fec504dbe0b0ddd40d0c3c2e02817f49e4"
|
|
9
9
|
},
|
|
10
10
|
"roots": {
|
|
11
11
|
"core": {
|
|
12
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
12
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.74-r194-npm-auth-recovery",
|
|
13
13
|
"deny_paths": [
|
|
14
14
|
"node_modules",
|
|
15
15
|
"controller/runtime",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.74",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "deadbyte-mcp",
|
|
9
|
-
"version": "0.15.
|
|
9
|
+
"version": "0.15.74",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"@modelcontextprotocol/server",
|
|
12
12
|
"@modelcontextprotocol/client",
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// R193 native Windows token probe. Diagnostic only: NOT owner consent or a grant.
|
|
2
|
+
// It can inspect itself or one PID. A caller must bind the returned kernel
|
|
3
|
+
// creation FILETIME to an independently trusted ownership ledger.
|
|
4
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
5
|
+
#define WIN32_LEAN_AND_MEAN
|
|
6
|
+
#endif
|
|
7
|
+
#include <windows.h>
|
|
8
|
+
#include <sddl.h>
|
|
9
|
+
#include <cstring>
|
|
10
|
+
#include <iomanip>
|
|
11
|
+
#include <iostream>
|
|
12
|
+
#include <limits>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
static bool info(HANDLE token, TOKEN_INFORMATION_CLASS klass, void* output, DWORD size) {
|
|
17
|
+
DWORD written = 0;
|
|
18
|
+
return GetTokenInformation(token, klass, output, size, &written) != 0 && written >= size;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
struct TokenReport {
|
|
22
|
+
bool valid = false;
|
|
23
|
+
bool elevated = false;
|
|
24
|
+
bool admin_enabled = false;
|
|
25
|
+
DWORD integrity_rid = 0;
|
|
26
|
+
TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
|
|
27
|
+
TOKEN_TYPE token_type = TokenPrimary;
|
|
28
|
+
DWORD session_id = 0;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static TokenReport inspect_token(HANDLE process) {
|
|
32
|
+
TokenReport out{};
|
|
33
|
+
HANDLE token = nullptr;
|
|
34
|
+
if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_DUPLICATE, &token)) return out;
|
|
35
|
+
TOKEN_ELEVATION elevation{};
|
|
36
|
+
TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
|
|
37
|
+
TOKEN_TYPE token_type = TokenPrimary;
|
|
38
|
+
DWORD session_id = 0;
|
|
39
|
+
bool valid = info(token, TokenElevation, &elevation, sizeof(elevation))
|
|
40
|
+
&& info(token, TokenElevationType, &elevation_type, sizeof(elevation_type))
|
|
41
|
+
&& info(token, TokenType, &token_type, sizeof(token_type))
|
|
42
|
+
&& info(token, TokenSessionId, &session_id, sizeof(session_id));
|
|
43
|
+
|
|
44
|
+
PSID administrators = nullptr;
|
|
45
|
+
BOOL admin_enabled = FALSE;
|
|
46
|
+
HANDLE membership_token = nullptr;
|
|
47
|
+
SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY;
|
|
48
|
+
if (valid) {
|
|
49
|
+
valid = AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
50
|
+
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administrators) != 0;
|
|
51
|
+
if (valid) valid = DuplicateToken(token, SecurityIdentification, &membership_token) != 0;
|
|
52
|
+
if (valid) valid = CheckTokenMembership(membership_token, administrators, &admin_enabled) != 0;
|
|
53
|
+
}
|
|
54
|
+
DWORD len = 0;
|
|
55
|
+
DWORD integrity_rid = 0;
|
|
56
|
+
if (valid) {
|
|
57
|
+
GetTokenInformation(token, TokenIntegrityLevel, nullptr, 0, &len);
|
|
58
|
+
valid = GetLastError() == ERROR_INSUFFICIENT_BUFFER
|
|
59
|
+
&& len >= sizeof(TOKEN_MANDATORY_LABEL) && len <= 65536;
|
|
60
|
+
if (valid) {
|
|
61
|
+
std::vector<unsigned char> buffer(len);
|
|
62
|
+
valid = GetTokenInformation(token, TokenIntegrityLevel, buffer.data(), len, &len) != 0;
|
|
63
|
+
if (valid) {
|
|
64
|
+
auto* label = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buffer.data());
|
|
65
|
+
auto* sid = label->Label.Sid;
|
|
66
|
+
valid = IsValidSid(sid) != 0 && *GetSidSubAuthorityCount(sid) >= 1;
|
|
67
|
+
if (valid) integrity_rid = *GetSidSubAuthority(sid, *GetSidSubAuthorityCount(sid)-1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (membership_token) CloseHandle(membership_token);
|
|
72
|
+
if (administrators) FreeSid(administrators);
|
|
73
|
+
CloseHandle(token);
|
|
74
|
+
out.valid = valid;
|
|
75
|
+
out.elevated = elevation.TokenIsElevated != 0;
|
|
76
|
+
out.admin_enabled = admin_enabled != FALSE;
|
|
77
|
+
out.integrity_rid = integrity_rid;
|
|
78
|
+
out.elevation_type = elevation_type;
|
|
79
|
+
out.token_type = token_type;
|
|
80
|
+
out.session_id = session_id;
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static bool standard_token(const TokenReport& report) {
|
|
85
|
+
return !report.elevated && !report.admin_enabled
|
|
86
|
+
&& report.token_type == TokenPrimary
|
|
87
|
+
&& report.integrity_rid >= SECURITY_MANDATORY_MEDIUM_RID
|
|
88
|
+
&& report.integrity_rid < SECURITY_MANDATORY_HIGH_RID
|
|
89
|
+
&& report.elevation_type != TokenElevationTypeFull;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
int main(int argc, char** argv) {
|
|
93
|
+
const bool report = argc == 2 && std::strcmp(argv[1], "--report") == 0;
|
|
94
|
+
const bool require_standard = argc == 2 && std::strcmp(argv[1], "--require-standard") == 0;
|
|
95
|
+
const bool inspect_pid = argc == 3 && std::strcmp(argv[1], "--inspect-pid") == 0;
|
|
96
|
+
if (!report && !require_standard && !inspect_pid) {
|
|
97
|
+
std::cerr << "usage: deadbyte-owner-token-r193.exe --report|--require-standard|--inspect-pid PID\n";
|
|
98
|
+
return 64;
|
|
99
|
+
}
|
|
100
|
+
if (inspect_pid) {
|
|
101
|
+
std::string raw(argv[2]);
|
|
102
|
+
size_t consumed = 0;
|
|
103
|
+
unsigned long long parsed = 0;
|
|
104
|
+
try { parsed = std::stoull(raw, &consumed, 10); }
|
|
105
|
+
catch (...) { std::cerr << "R193_TARGET_PID_INVALID\n"; return 64; }
|
|
106
|
+
if (consumed != raw.size() || parsed < 1 || parsed > std::numeric_limits<DWORD>::max()) {
|
|
107
|
+
std::cerr << "R193_TARGET_PID_INVALID\n";
|
|
108
|
+
return 64;
|
|
109
|
+
}
|
|
110
|
+
const DWORD pid = static_cast<DWORD>(parsed);
|
|
111
|
+
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
|
112
|
+
if (!process) { std::cerr << "R193_TARGET_PROCESS_UNAVAILABLE\n"; return 72; }
|
|
113
|
+
FILETIME created{}, exited{}, kernel{}, user{};
|
|
114
|
+
const bool times = GetProcessTimes(process, &created, &exited, &kernel, &user) != 0;
|
|
115
|
+
const TokenReport target = inspect_token(process);
|
|
116
|
+
CloseHandle(process);
|
|
117
|
+
if (!times || !target.valid) {
|
|
118
|
+
std::cerr << "R193_TARGET_TOKEN_INSPECTION_FAILED\n";
|
|
119
|
+
return 73;
|
|
120
|
+
}
|
|
121
|
+
ULARGE_INTEGER identity{};
|
|
122
|
+
identity.LowPart = created.dwLowDateTime;
|
|
123
|
+
identity.HighPart = created.dwHighDateTime;
|
|
124
|
+
std::cout << "{\"schema\":\"deadbyte.r193-native-process-token.v1\",\"pid\":" << pid
|
|
125
|
+
<< ",\"creation_filetime_hex\":\"" << std::hex << std::setfill('0') << std::setw(16)
|
|
126
|
+
<< identity.QuadPart << std::dec
|
|
127
|
+
<< "\",\"alive\":true,\"identity_verified\":true,\"elevated\":"
|
|
128
|
+
<< (target.elevated ? "true" : "false")
|
|
129
|
+
<< ",\"admin_enabled\":" << (target.admin_enabled ? "true" : "false")
|
|
130
|
+
<< ",\"integrity_rid\":" << target.integrity_rid
|
|
131
|
+
<< ",\"elevation_type\":" << static_cast<int>(target.elevation_type)
|
|
132
|
+
<< ",\"token_type\":\"" << (target.token_type == TokenPrimary ? "primary" : "impersonation")
|
|
133
|
+
<< "\",\"session_id\":" << target.session_id
|
|
134
|
+
<< ",\"owner_approved\":false,\"authority_granted\":false}\n";
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const TokenReport self = inspect_token(GetCurrentProcess());
|
|
139
|
+
if (!self.valid) {
|
|
140
|
+
std::cerr << "R193_TOKEN_INSPECTION_FAILED\n";
|
|
141
|
+
return 71;
|
|
142
|
+
}
|
|
143
|
+
const bool standard = standard_token(self);
|
|
144
|
+
const char* reason = standard ? "STANDARD_TOKEN_OBSERVED" : "NOT_STANDARD_TOKEN";
|
|
145
|
+
std::cout << "{\"schema\":\"deadbyte.r193-native-token.v1\",\"status\":\""
|
|
146
|
+
<< (standard ? "STANDARD_TOKEN_OBSERVED" : "BLOCKED")
|
|
147
|
+
<< "\",\"reason\":\"" << reason
|
|
148
|
+
<< "\",\"elevated\":" << (self.elevated ? "true" : "false")
|
|
149
|
+
<< ",\"admin_enabled\":" << (self.admin_enabled ? "true" : "false")
|
|
150
|
+
<< ",\"integrity_rid\":" << self.integrity_rid
|
|
151
|
+
<< ",\"elevation_type\":" << static_cast<int>(self.elevation_type)
|
|
152
|
+
<< ",\"token_type\":" << static_cast<int>(self.token_type)
|
|
153
|
+
<< ",\"session_id\":" << self.session_id
|
|
154
|
+
<< ",\"parent_independence_verified\":false,\"owner_approved\":false,\"authority_granted\":false}\n";
|
|
155
|
+
return require_standard && !standard ? 3 : 0;
|
|
156
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.74",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"r67:shard:run": "node scripts/r67-regression-shard-run.mjs",
|
|
96
96
|
"r67:shard:verify": "node scripts/r67-regression-reconcile.mjs",
|
|
97
97
|
"r167:focused:test": "node --test --test-concurrency=1 test/r167-activation-path.test.mjs test/r167-release-contract.test.mjs test/r167-release-readiness.test.mjs test/r160-release-contract.test.mjs test/r158-epoch-bootstrap.test.mjs test/r65-activation-orchestration.test.mjs",
|
|
98
|
-
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
98
|
+
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r194.ps1 -Stage all",
|
|
99
99
|
"r168:focused:test": "node test/r168-combined-targeted-runner.mjs",
|
|
100
100
|
"r168:release:verify": "node scripts/npm-release-r48.mjs --verify-only",
|
|
101
101
|
"r172:focused:test": "node --test --test-concurrency=1 test/r172-activation-recovery.test.mjs test/r172-release-contract.test.mjs test/r169-delegate-active.test.mjs test/r169-delegate-cli-integration.test.mjs test/r65-activation-lock-recovery.test.mjs test/r65-activation-orchestration.test.mjs",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
[CmdletBinding()]
|
|
3
|
+
param(
|
|
4
|
+
[ValidateSet('preflight','focused','full','manifest','doctor','all')][string]$Stage='preflight'
|
|
5
|
+
)
|
|
6
|
+
Set-StrictMode -Version Latest
|
|
7
|
+
$ErrorActionPreference='Stop'
|
|
8
|
+
$repo=Split-Path -Parent $PSScriptRoot
|
|
9
|
+
$productionState=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
10
|
+
$parentId='v0.15.72-0a5d9436e06e2d9e'
|
|
11
|
+
$parentSha='0a5d9436e06e2d9e7cf9179417465955d8ca5ba88655b13dd019190243c1cc90'
|
|
12
|
+
$parentGen='aec7b998306a3bbbcc59088648e7bc46ca7e203d749d4550b5b5f372032f92b0'
|
|
13
|
+
$parentTarball='15ab5d9808cbca342aee3f64c6d1123e8fbb72964674eab460e355c8675bf84d'
|
|
14
|
+
$nativeNode='C:\Program Files\nodejs\node.exe'
|
|
15
|
+
$node=if(Test-Path -LiteralPath $nativeNode -PathType Leaf){$nativeNode}else{(Get-Command node -ErrorAction Stop).Source}
|
|
16
|
+
function Sha([string]$file){
|
|
17
|
+
$sha=[Security.Cryptography.SHA256]::Create()
|
|
18
|
+
try{$stream=[IO.File]::OpenRead($file);try{return ([BitConverter]::ToString($sha.ComputeHash($stream))).Replace('-','').ToLowerInvariant()}finally{$stream.Dispose()}}finally{$sha.Dispose()}
|
|
19
|
+
}
|
|
20
|
+
function Need([bool]$condition,[string]$message){if(-not $condition){throw $message}}
|
|
21
|
+
function Run-Node([string]$label,[string[]]$nodeArgs){
|
|
22
|
+
Need ($null -ne $nodeArgs -and $nodeArgs.Count -gt 0) "R193_${label}_NO_NODE_ARGUMENTS"
|
|
23
|
+
Write-Output "R193_STAGE_START=$label"
|
|
24
|
+
$global:LASTEXITCODE=0
|
|
25
|
+
& $node @nodeArgs
|
|
26
|
+
$exit=$global:LASTEXITCODE
|
|
27
|
+
if($exit -ne 0){throw "R193_${label}_FAILED exit=$exit"}
|
|
28
|
+
Write-Output "R193_STAGE_PASS=$label"
|
|
29
|
+
}
|
|
30
|
+
Push-Location $repo
|
|
31
|
+
$previousExpectedDevelopmentRoot=$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT
|
|
32
|
+
try{
|
|
33
|
+
$pkg=Get-Content -LiteralPath 'package.json' -Raw | ConvertFrom-Json
|
|
34
|
+
$release=Get-Content -LiteralPath 'RELEASE.json' -Raw | ConvertFrom-Json
|
|
35
|
+
Need (-not [string]::IsNullOrWhiteSpace([string]$release.r192.development_policy_root)) 'R193_DEVELOPMENT_PROVENANCE_ROOT_MISSING'
|
|
36
|
+
$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=[IO.Path]::GetFullPath([string]$release.r192.development_policy_root)
|
|
37
|
+
Need ($pkg.version -eq '0.15.73' -and $release.version -eq '0.15.73' -and $release.train -eq 'R193') 'R193_CANDIDATE_VERSION_DRIFT'
|
|
38
|
+
Need ($release.source_predecessor.version -eq '0.15.72' -and $release.source_predecessor.train -eq 'R192') 'R193_SOURCE_PREDECESSOR_VERSION_DRIFT'
|
|
39
|
+
Need ($release.source_predecessor.manifest_sha256 -eq $parentSha -and $release.source_predecessor.tarball_sha256 -eq $parentTarball) 'R193_SOURCE_PREDECESSOR_IDENTITY_DRIFT'
|
|
40
|
+
Need ($release.runtime_predecessor.release_id -eq $parentId -and $release.runtime_predecessor.manifest_sha256 -eq $parentSha -and $release.runtime_predecessor.generation_manifest_sha256 -eq $parentGen) 'R193_RUNTIME_PREDECESSOR_IDENTITY_DRIFT'
|
|
41
|
+
$active=Get-Content -LiteralPath (Join-Path $productionState 'active-release.json') -Raw | ConvertFrom-Json
|
|
42
|
+
Need ($active.version -eq '0.15.72' -and $active.release_id -eq $parentId -and $active.manifest_sha256 -eq $parentSha -and $active.generation_manifest_sha256 -eq $parentGen) 'R193_ACTIVE_RUNTIME_DRIFT'
|
|
43
|
+
Need ((Sha (Join-Path $active.release_root 'MANIFEST.SHA256')) -eq $parentSha) 'R193_ACTIVE_MANIFEST_MISMATCH'
|
|
44
|
+
Write-Output 'R193_PREDECESSOR_PREFLIGHT=PASS'
|
|
45
|
+
if($Stage -eq 'preflight'){return}
|
|
46
|
+
if($Stage -in @('focused','all')){
|
|
47
|
+
$focused=@(
|
|
48
|
+
'test/r70-owner-approval.test.mjs',
|
|
49
|
+
'test/r89-privileged-install-boundary.test.mjs',
|
|
50
|
+
'test/r186-controller-owner-grant.test.mjs',
|
|
51
|
+
'test/r186-owner-consent-preflight.test.mjs',
|
|
52
|
+
'test/r186-owner-enrollment.test.mjs',
|
|
53
|
+
'test/r186-owner-grant-broker.test.mjs',
|
|
54
|
+
'test/r186-owner-local-signing.test.mjs',
|
|
55
|
+
'test/r186-owner-request.test.mjs',
|
|
56
|
+
'test/r186-owner-review-export.test.mjs',
|
|
57
|
+
'test/r188-owner-uac.test.mjs',
|
|
58
|
+
'test/r188-uac-boundary.test.mjs',
|
|
59
|
+
'test/r193-limited-migration-contract.test.mjs',
|
|
60
|
+
'test/r193-live-owner-subject.test.mjs',
|
|
61
|
+
'test/r193-native-process-observer.test.mjs',
|
|
62
|
+
'test/r193-native-token.test.mjs',
|
|
63
|
+
'test/r193-owner-enrollment-entrypoint.test.mjs',
|
|
64
|
+
'test/r193-owner-secret-acl.test.mjs',
|
|
65
|
+
'test/r193-owner-uac-broker.test.mjs',
|
|
66
|
+
'test/r193-owner-uac-entrypoint.test.mjs',
|
|
67
|
+
'test/r193-release-readiness.test.mjs',
|
|
68
|
+
'test/r193-release-identity.test.mjs',
|
|
69
|
+
'test/r193-uac-r70-bridge.test.mjs',
|
|
70
|
+
'test/r193-windows-gate.test.mjs'
|
|
71
|
+
)
|
|
72
|
+
Run-Node -label 'focused' -nodeArgs (@('--test','--test-reporter=tap','--test-concurrency=1')+$focused)
|
|
73
|
+
if($Stage -eq 'focused'){return}
|
|
74
|
+
}
|
|
75
|
+
if($Stage -in @('full','all')){
|
|
76
|
+
$previousCrossVolume=$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT
|
|
77
|
+
$tempDrive=[IO.Path]::GetPathRoot([IO.Path]::GetFullPath($env:TEMP))
|
|
78
|
+
$scratchDrive=if($tempDrive -ieq 'D:\'){'C:\'}else{'D:\'}
|
|
79
|
+
Need (Test-Path -LiteralPath $scratchDrive -PathType Container) 'R193_CROSS_VOLUME_DRIVE_UNAVAILABLE'
|
|
80
|
+
$crossVolume=Join-Path $scratchDrive 'DEADBYTE-R193-CrossVolume'
|
|
81
|
+
try{
|
|
82
|
+
[IO.Directory]::CreateDirectory($crossVolume)|Out-Null
|
|
83
|
+
$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$crossVolume
|
|
84
|
+
Run-Node -label 'full' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs')
|
|
85
|
+
}finally{$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$previousCrossVolume}
|
|
86
|
+
if($Stage -eq 'full'){return}
|
|
87
|
+
}
|
|
88
|
+
if($Stage -in @('manifest','all')){
|
|
89
|
+
Run-Node -label 'manifest' -nodeArgs @('scripts/release-manifest.mjs','verify')
|
|
90
|
+
if($Stage -eq 'manifest'){return}
|
|
91
|
+
}
|
|
92
|
+
if($Stage -in @('doctor','all')){
|
|
93
|
+
$previousStateRoot=$env:DEADBYTE_STATE_ROOT
|
|
94
|
+
$isolated=Join-Path $env:TEMP ('deadbyte-r193-doctor-'+[guid]::NewGuid().ToString('N'))
|
|
95
|
+
try{
|
|
96
|
+
[IO.Directory]::CreateDirectory($isolated)|Out-Null
|
|
97
|
+
$env:DEADBYTE_STATE_ROOT=$isolated
|
|
98
|
+
Run-Node -label 'isolated_doctor' -nodeArgs @('src/deadbyte-cli.mjs','doctor')
|
|
99
|
+
}finally{
|
|
100
|
+
$env:DEADBYTE_STATE_ROOT=$previousStateRoot
|
|
101
|
+
Remove-Item -LiteralPath $isolated -Recurse -Force -ErrorAction SilentlyContinue
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
Write-Output 'R193_WINDOWS_CANDIDATE_GATE=PASS_PREPUBLICATION'
|
|
105
|
+
}finally{
|
|
106
|
+
$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$previousExpectedDevelopmentRoot
|
|
107
|
+
Pop-Location
|
|
108
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
[CmdletBinding()]
|
|
3
|
+
param(
|
|
4
|
+
[ValidateSet('preflight','focused','full','manifest','doctor','all')][string]$Stage='preflight'
|
|
5
|
+
)
|
|
6
|
+
Set-StrictMode -Version Latest
|
|
7
|
+
$ErrorActionPreference='Stop'
|
|
8
|
+
$repo=Split-Path -Parent $PSScriptRoot
|
|
9
|
+
$productionState=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
10
|
+
$parentId='v0.15.72-0a5d9436e06e2d9e'
|
|
11
|
+
$parentSha='0a5d9436e06e2d9e7cf9179417465955d8ca5ba88655b13dd019190243c1cc90'
|
|
12
|
+
$parentGen='aec7b998306a3bbbcc59088648e7bc46ca7e203d749d4550b5b5f372032f92b0'
|
|
13
|
+
$parentTarball='15ab5d9808cbca342aee3f64c6d1123e8fbb72964674eab460e355c8675bf84d'
|
|
14
|
+
$nativeNode='C:\Program Files\nodejs\node.exe'
|
|
15
|
+
$node=if(Test-Path -LiteralPath $nativeNode -PathType Leaf){$nativeNode}else{(Get-Command node -ErrorAction Stop).Source}
|
|
16
|
+
function Sha([string]$file){
|
|
17
|
+
$sha=[Security.Cryptography.SHA256]::Create()
|
|
18
|
+
try{$stream=[IO.File]::OpenRead($file);try{return ([BitConverter]::ToString($sha.ComputeHash($stream))).Replace('-','').ToLowerInvariant()}finally{$stream.Dispose()}}finally{$sha.Dispose()}
|
|
19
|
+
}
|
|
20
|
+
function Need([bool]$condition,[string]$message){if(-not $condition){throw $message}}
|
|
21
|
+
function Run-Node([string]$label,[string[]]$nodeArgs){
|
|
22
|
+
Need ($null -ne $nodeArgs -and $nodeArgs.Count -gt 0) "R194_${label}_NO_NODE_ARGUMENTS"
|
|
23
|
+
Write-Output "R194_STAGE_START=$label"
|
|
24
|
+
$global:LASTEXITCODE=0
|
|
25
|
+
& $node @nodeArgs
|
|
26
|
+
$exit=$global:LASTEXITCODE
|
|
27
|
+
if($exit -ne 0){throw "R194_${label}_FAILED exit=$exit"}
|
|
28
|
+
Write-Output "R194_STAGE_PASS=$label"
|
|
29
|
+
}
|
|
30
|
+
Push-Location $repo
|
|
31
|
+
$previousExpectedDevelopmentRoot=$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT
|
|
32
|
+
try{
|
|
33
|
+
$pkg=Get-Content -LiteralPath 'package.json' -Raw | ConvertFrom-Json
|
|
34
|
+
$release=Get-Content -LiteralPath 'RELEASE.json' -Raw | ConvertFrom-Json
|
|
35
|
+
Need (-not [string]::IsNullOrWhiteSpace([string]$release.r194.development_policy_root)) 'R194_DEVELOPMENT_PROVENANCE_ROOT_MISSING'
|
|
36
|
+
$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=[IO.Path]::GetFullPath([string]$release.r194.development_policy_root)
|
|
37
|
+
Need ($pkg.version -eq '0.15.74' -and $release.version -eq '0.15.74' -and $release.train -eq 'R194') 'R194_CANDIDATE_VERSION_DRIFT'
|
|
38
|
+
Need ($release.source_predecessor.version -eq '0.15.73' -and $release.source_predecessor.train -eq 'R193') 'R194_SOURCE_PREDECESSOR_VERSION_DRIFT'
|
|
39
|
+
Need ($release.source_predecessor.manifest_sha256 -eq 'e0b3f8b333f1c1260a97b178d1a8beff5742355296a9fe7daba378612604a3bd' -and $release.source_predecessor.tarball_sha256 -eq '13c3dd23a535609aa53173b328c7d9299d5dcada684f3253a88af6a4b300c534') 'R194_SOURCE_PREDECESSOR_IDENTITY_DRIFT'
|
|
40
|
+
Need ($release.runtime_predecessor.release_id -eq $parentId -and $release.runtime_predecessor.manifest_sha256 -eq $parentSha -and $release.runtime_predecessor.generation_manifest_sha256 -eq $parentGen) 'R194_RUNTIME_PREDECESSOR_IDENTITY_DRIFT'
|
|
41
|
+
$active=Get-Content -LiteralPath (Join-Path $productionState 'active-release.json') -Raw | ConvertFrom-Json
|
|
42
|
+
Need ($active.version -eq '0.15.72' -and $active.release_id -eq $parentId -and $active.manifest_sha256 -eq $parentSha -and $active.generation_manifest_sha256 -eq $parentGen) 'R194_ACTIVE_RUNTIME_DRIFT'
|
|
43
|
+
Need ((Sha (Join-Path $active.release_root 'MANIFEST.SHA256')) -eq $parentSha) 'R194_ACTIVE_MANIFEST_MISMATCH'
|
|
44
|
+
Write-Output 'R194_PREDECESSOR_PREFLIGHT=PASS'
|
|
45
|
+
if($Stage -eq 'preflight'){return}
|
|
46
|
+
if($Stage -in @('focused','all')){
|
|
47
|
+
$focused=@(
|
|
48
|
+
'test/r70-owner-approval.test.mjs',
|
|
49
|
+
'test/r89-privileged-install-boundary.test.mjs',
|
|
50
|
+
'test/r186-controller-owner-grant.test.mjs',
|
|
51
|
+
'test/r186-owner-consent-preflight.test.mjs',
|
|
52
|
+
'test/r186-owner-enrollment.test.mjs',
|
|
53
|
+
'test/r186-owner-grant-broker.test.mjs',
|
|
54
|
+
'test/r186-owner-local-signing.test.mjs',
|
|
55
|
+
'test/r186-owner-request.test.mjs',
|
|
56
|
+
'test/r186-owner-review-export.test.mjs',
|
|
57
|
+
'test/r188-owner-uac.test.mjs',
|
|
58
|
+
'test/r188-uac-boundary.test.mjs',
|
|
59
|
+
'test/r193-limited-migration-contract.test.mjs',
|
|
60
|
+
'test/r193-live-owner-subject.test.mjs',
|
|
61
|
+
'test/r193-native-process-observer.test.mjs',
|
|
62
|
+
'test/r193-native-token.test.mjs',
|
|
63
|
+
'test/r193-owner-enrollment-entrypoint.test.mjs',
|
|
64
|
+
'test/r193-owner-secret-acl.test.mjs',
|
|
65
|
+
'test/r193-owner-uac-broker.test.mjs',
|
|
66
|
+
'test/r193-owner-uac-entrypoint.test.mjs',
|
|
67
|
+
'test/r193-release-readiness.test.mjs',
|
|
68
|
+
'test/r193-release-identity.test.mjs',
|
|
69
|
+
'test/r194-release-identity.test.mjs',
|
|
70
|
+
'test/r194-release-readiness.test.mjs',
|
|
71
|
+
'test/r193-uac-r70-bridge.test.mjs',
|
|
72
|
+
'test/r194-windows-gate.test.mjs',
|
|
73
|
+
'test/r194-npm-auth-recovery.test.mjs',
|
|
74
|
+
'test/r194-npm-preauth.test.mjs'
|
|
75
|
+
)
|
|
76
|
+
Run-Node -label 'focused' -nodeArgs (@('--test','--test-reporter=tap','--test-concurrency=1')+$focused)
|
|
77
|
+
if($Stage -eq 'focused'){return}
|
|
78
|
+
}
|
|
79
|
+
if($Stage -in @('full','all')){
|
|
80
|
+
$previousCrossVolume=$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT
|
|
81
|
+
$tempDrive=[IO.Path]::GetPathRoot([IO.Path]::GetFullPath($env:TEMP))
|
|
82
|
+
$scratchDrive=if($tempDrive -ieq 'D:\'){'C:\'}else{'D:\'}
|
|
83
|
+
Need (Test-Path -LiteralPath $scratchDrive -PathType Container) 'R194_CROSS_VOLUME_DRIVE_UNAVAILABLE'
|
|
84
|
+
$crossVolume=Join-Path $scratchDrive 'DEADBYTE-R194-CrossVolume'
|
|
85
|
+
try{
|
|
86
|
+
[IO.Directory]::CreateDirectory($crossVolume)|Out-Null
|
|
87
|
+
$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$crossVolume
|
|
88
|
+
Run-Node -label 'full' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs')
|
|
89
|
+
}finally{$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$previousCrossVolume}
|
|
90
|
+
if($Stage -eq 'full'){return}
|
|
91
|
+
}
|
|
92
|
+
if($Stage -in @('manifest','all')){
|
|
93
|
+
Run-Node -label 'manifest' -nodeArgs @('scripts/release-manifest.mjs','verify')
|
|
94
|
+
if($Stage -eq 'manifest'){return}
|
|
95
|
+
}
|
|
96
|
+
if($Stage -in @('doctor','all')){
|
|
97
|
+
$previousStateRoot=$env:DEADBYTE_STATE_ROOT
|
|
98
|
+
$isolated=Join-Path $env:TEMP ('deadbyte-r194-doctor-'+[guid]::NewGuid().ToString('N'))
|
|
99
|
+
try{
|
|
100
|
+
[IO.Directory]::CreateDirectory($isolated)|Out-Null
|
|
101
|
+
$env:DEADBYTE_STATE_ROOT=$isolated
|
|
102
|
+
Run-Node -label 'isolated_doctor' -nodeArgs @('src/deadbyte-cli.mjs','doctor')
|
|
103
|
+
}finally{
|
|
104
|
+
$env:DEADBYTE_STATE_ROOT=$previousStateRoot
|
|
105
|
+
Remove-Item -LiteralPath $isolated -Recurse -Force -ErrorAction SilentlyContinue
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
Write-Output 'R194_WINDOWS_CANDIDATE_GATE=PASS_PREPUBLICATION'
|
|
109
|
+
}finally{
|
|
110
|
+
$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$previousExpectedDevelopmentRoot
|
|
111
|
+
Pop-Location
|
|
112
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// R194 publication failure boundary: do not infer auth failure from a generic 404.
|
|
2
|
+
// Only an explicit npm auth code, or the exact npm registry PUT permission refusal,
|
|
3
|
+
// can be treated as a definitively rejected submission.
|
|
4
|
+
const AUTH_CODE = /(?:^|\s)(?:E401|E403|EOTP|ENEEDAUTH|EAUTH)(?:\s|$)/i;
|
|
5
|
+
const PERMISSION = /(?:you do not have permission to access it|you (?:don't|do not) have permission to publish|permission denied|forbidden|not authorized)/i;
|
|
6
|
+
const REGISTRY_PUT = /\b404\s+Not\s+Found\s+-\s+PUT\s+https:\/\/registry\.npmjs\.org\/deadbyte-mcp(?:\s|\?|\b)/i;
|
|
7
|
+
export function classifyNpmPublishFailure({exitCode,output}={}){
|
|
8
|
+
if(exitCode===0)return 'SUBMISSION_ACCEPTED';
|
|
9
|
+
const text=typeof output==='string'?output.slice(-65536):'';
|
|
10
|
+
if(AUTH_CODE.test(text))return 'EAUTH';
|
|
11
|
+
if(REGISTRY_PUT.test(text)&&PERMISSION.test(text))return 'EAUTH';
|
|
12
|
+
return 'OUTCOME_UNKNOWN';
|
|
13
|
+
}
|
|
@@ -55,7 +55,7 @@ function result(state,extra={}){return Object.freeze({state,...extra});}
|
|
|
55
55
|
// The no-clobber claim is the dispatch fence: once it exists, no invocation may
|
|
56
56
|
// submit the same immutable npm version again, even if the first process dies.
|
|
57
57
|
export async function reconcileNpmPublication({recordPath,version,manifestSha256,localBytes,publish,lookup,tags,fetchTarball,
|
|
58
|
-
timeoutMs=180000,pollMs=1500,sleep=pause,now=()=>performance.now()}){
|
|
58
|
+
timeoutMs=180000,pollMs=1500,sleep=pause,now=()=>performance.now(),authorizeAuthRetry=null,authorizePublish=null}){
|
|
59
59
|
if(!path.isAbsolute(recordPath)||!/^\d+\.\d+\.\d+$/.test(version)||!HEX64.test(String(manifestSha256))||!Buffer.isBuffer(localBytes)||
|
|
60
60
|
!Number.isSafeInteger(timeoutMs)||timeoutMs<0||timeoutMs>900000||!Number.isSafeInteger(pollMs)||pollMs<1||pollMs>30000||
|
|
61
61
|
[publish,lookup,tags,fetchTarball,sleep,now].some(fn=>typeof fn!=='function'))throw new Error('npm publication reconciliation inputs invalid');
|
|
@@ -97,6 +97,14 @@ export async function reconcileNpmPublication({recordPath,version,manifestSha256
|
|
|
97
97
|
}
|
|
98
98
|
if(!claim){
|
|
99
99
|
if(observed)return observed; // Registry outage: do not submit while identity is unknown.
|
|
100
|
+
// R194: check authenticated package ownership before creating the irreversible claim.
|
|
101
|
+
if(authorizePublish!==null){
|
|
102
|
+
if(typeof authorizePublish!=='function')throw new Error('npm owner preflight invalid');
|
|
103
|
+
let authorized=false;
|
|
104
|
+
try{authorized=(await authorizePublish(Object.freeze({version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256})))===true;}
|
|
105
|
+
catch{return result('AUTH_REQUIRED',{reason:'owner authentication preflight failed before submission'});}
|
|
106
|
+
if(!authorized)return result('AUTH_REQUIRED',{reason:'owner authentication preflight is not confirmed'});
|
|
107
|
+
}
|
|
100
108
|
let created=false;
|
|
101
109
|
try{
|
|
102
110
|
await publishClaim(claimFile,{schema:'deadbyte.npm-publication-claim.r65.v1',version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256,created_at_utc:new Date().toISOString()});
|
|
@@ -114,15 +122,67 @@ export async function reconcileNpmPublication({recordPath,version,manifestSha256
|
|
|
114
122
|
await publish();
|
|
115
123
|
await event(recordPath,'SUBMISSION_ACCEPTED',{tarball_sha256:tarballSha256});
|
|
116
124
|
}catch(error){
|
|
117
|
-
const auth=/E401|E403|EOTP|authentication|one-time password|2FA/i.test(String(error?.code??'')+' '+String(error?.message??''));
|
|
125
|
+
const auth=/E401|E403|EOTP|EAUTH|ENEEDAUTH|authentication|one-time password|2FA/i.test(String(error?.code??'')+' '+String(error?.message??''));
|
|
118
126
|
await event(recordPath,auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{error_code:String(error?.code??'UNKNOWN').slice(0,64)});
|
|
119
127
|
return result(auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{reason:String(error?.code??error?.message??error)});
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
}
|
|
123
131
|
const states=await priorState(recordPath);
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
const authRejected=states.some(item=>item.state==='AUTH_REQUIRED');
|
|
133
|
+
let accepted=states.some(item=>item.state==='SUBMISSION_ACCEPTED');
|
|
134
|
+
if(authRejected&&!accepted){
|
|
135
|
+
// An original ambiguous outcome cannot become retryable via a later login.
|
|
136
|
+
if(states.some(item=>item.state==='OUTCOME_UNKNOWN'))
|
|
137
|
+
return result('OUTCOME_UNKNOWN',{reason:'submission outcome is ambiguous; original claim retained'});
|
|
138
|
+
const retryFile=path.join(recordPath,'auth-retry-claim.json');
|
|
139
|
+
const retry=await readClaim(retryFile);
|
|
140
|
+
if(retry?.incomplete)return result('OUTCOME_UNKNOWN',{reason:'incomplete auth retry claim'});
|
|
141
|
+
if(retry){
|
|
142
|
+
if(retry.schema!=='deadbyte.npm-auth-retry-claim.r194.v1'||retry.version!==version||
|
|
143
|
+
retry.manifest_sha256!==manifestSha256||retry.tarball_sha256!==tarballSha256)
|
|
144
|
+
return result('REGISTRY_REJECTED',{reason:'auth retry claim artifact mismatch'});
|
|
145
|
+
return result('AUTH_REQUIRED',{reason:'one-time auth retry was already dispatched or claimed; do not retry'});
|
|
146
|
+
}
|
|
147
|
+
if(typeof authorizeAuthRetry!=='function')return result('AUTH_REQUIRED');
|
|
148
|
+
let allowed=false;
|
|
149
|
+
try{
|
|
150
|
+
allowed=(await authorizeAuthRetry(Object.freeze({
|
|
151
|
+
version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256
|
|
152
|
+
})))===true;
|
|
153
|
+
}catch{return result('AUTH_REQUIRED',{reason:'fresh authenticated owner check failed'});}
|
|
154
|
+
if(!allowed)return result('AUTH_REQUIRED',{reason:'authenticated owner preflight not confirmed'});
|
|
155
|
+
// Re-read registry just before the one-time retry fence.
|
|
156
|
+
const beforeRetry=await check();
|
|
157
|
+
if(beforeRetry?.state==='REGISTRY_IDENTITY_VERIFIED'||beforeRetry?.state==='REGISTRY_REJECTED')return beforeRetry;
|
|
158
|
+
if(beforeRetry?.state==='OUTCOME_UNKNOWN')return beforeRetry;
|
|
159
|
+
try{
|
|
160
|
+
await writeExclusive(retryFile,{
|
|
161
|
+
schema:'deadbyte.npm-auth-retry-claim.r194.v1',version,
|
|
162
|
+
manifest_sha256:manifestSha256,tarball_sha256:tarballSha256,
|
|
163
|
+
created_at_utc:new Date().toISOString()
|
|
164
|
+
});
|
|
165
|
+
}catch(error){
|
|
166
|
+
if(error?.code==='EEXIST')return result('AUTH_REQUIRED',{reason:'concurrent auth retry already claimed; do not dispatch'});
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
// A durable retry claim is written BEFORE any additional network effect.
|
|
170
|
+
await event(recordPath,'AUTH_RETRY_STARTED',{tarball_sha256:tarballSha256});
|
|
171
|
+
try{
|
|
172
|
+
await publish();
|
|
173
|
+
await event(recordPath,'SUBMISSION_ACCEPTED',{tarball_sha256:tarballSha256,retry:'auth-only'});
|
|
174
|
+
accepted=true;
|
|
175
|
+
}catch(error){
|
|
176
|
+
const auth=/E401|E403|EOTP|EAUTH|ENEEDAUTH|authentication|one-time password|2FA/i.test(
|
|
177
|
+
String(error?.code??'')+' '+String(error?.message??''));
|
|
178
|
+
await event(recordPath,auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{
|
|
179
|
+
error_code:String(error?.code??'UNKNOWN').slice(0,64),retry:'auth-only'
|
|
180
|
+
});
|
|
181
|
+
return result(auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{
|
|
182
|
+
reason:'one-time auth retry rejected or unverified; retry claim retained'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
126
186
|
const started=now();
|
|
127
187
|
do{
|
|
128
188
|
observed=await check();
|