fix: freeze dev ports and edge health
This commit is contained in:
@@ -27,7 +27,12 @@ function proxyDetails() {
|
||||
const server = createServer((request, response) => {
|
||||
const url = new URL(request.url || "/", "http://hwlab-edge-proxy.local");
|
||||
|
||||
if (request.method === "GET" && (url.pathname === "/health" || url.pathname === "/health/live")) {
|
||||
if (request.method === "GET" && url.pathname === "/health/live") {
|
||||
proxyHttpRequest({ request, response, upstream });
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method === "GET" && (url.pathname === "/health" || url.pathname === "/edge/health")) {
|
||||
sendJson(response, 200, healthPayload({
|
||||
serviceId,
|
||||
role: "public-dev-ingress",
|
||||
@@ -56,7 +61,7 @@ const server = createServer((request, response) => {
|
||||
if (request.method === "GET" && url.pathname === "/help") {
|
||||
sendJson(response, 200, {
|
||||
serviceId,
|
||||
commands: ["GET /health", "GET /health/live", "GET /status", "GET /routes"]
|
||||
commands: ["GET /health", "GET /edge/health", "GET /health/live", "GET /status", "GET /routes"]
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
const serviceId = "hwlab-tunnel-client";
|
||||
const mode = process.env.HWLAB_TUNNEL_MODE || "frp-client";
|
||||
const serverAddress = process.env.HWLAB_FRP_SERVER_ADDR || "74.48.78.17";
|
||||
const publicPort = parsePort(process.env.HWLAB_FRP_PUBLIC_PORT, 6667);
|
||||
const publicPort = parsePort(process.env.HWLAB_FRP_PUBLIC_PORT, 16667);
|
||||
const webPublicPort = parsePort(process.env.HWLAB_FRP_WEB_PUBLIC_PORT, 16666);
|
||||
const localService = process.env.HWLAB_TUNNEL_LOCAL_SERVICE || "hwlab-edge-proxy";
|
||||
const localPort = parsePort(process.env.HWLAB_TUNNEL_LOCAL_PORT, 6667);
|
||||
const { host, port } = resolveHostPort({
|
||||
@@ -29,6 +30,7 @@ function tunnelState() {
|
||||
direction: "d601-to-master",
|
||||
serverAddress,
|
||||
publicPort,
|
||||
webPublicPort,
|
||||
localService,
|
||||
localPort
|
||||
},
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
`deploy.schema.json` defines the future `deploy/deploy.json` manifest shape.
|
||||
|
||||
The MVP acceptance environment is DEV only, with endpoint
|
||||
`http://74.48.78.17:6667`. PROD profile fields may be represented in schema for
|
||||
`http://74.48.78.17:16667`. PROD profile fields may be represented in schema for
|
||||
future compatibility, but PROD deployment is not part of MVP acceptance.
|
||||
|
||||
## L5 DEV Skeleton
|
||||
@@ -27,7 +27,7 @@ Next DEV deploy smoke commands, for a separately authorized deployment task:
|
||||
npm run check
|
||||
node -e "JSON.parse(require('node:fs').readFileSync('deploy/deploy.json','utf8'))"
|
||||
kubectl apply --dry-run=server -k deploy/k8s/dev
|
||||
curl -fsS http://74.48.78.17:6667/health/live
|
||||
curl -fsS http://74.48.78.17:16667/health/live
|
||||
node scripts/dev-edge-health-smoke.mjs --live --write-report
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"const": "http://74.48.78.17:6667"
|
||||
"const": "http://74.48.78.17:16667"
|
||||
},
|
||||
"profiles": {
|
||||
"type": "object",
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
The DEV reverse link is D601 to master edge through frp.
|
||||
|
||||
- Public DEV endpoint: `http://74.48.78.17:6667`
|
||||
- Public DEV endpoint: `http://74.48.78.17:16667`
|
||||
- `hwlab-tunnel-client` owns the frp client health contract.
|
||||
- `hwlab-edge-proxy` owns public HTTP ingress on port `6667`.
|
||||
- `frps.dev.toml` reserves `6667` only as a TCP `remotePort`; do not also bind
|
||||
it as `vhostHTTPPort`, because that collides with the DEV TCP proxy.
|
||||
- `hwlab-cloud-web` owns the public browser entry on port `16666`.
|
||||
- `hwlab-edge-proxy` owns public API/health ingress on port `16667`.
|
||||
- `frps.dev.toml` reserves `16666` and `16667` only as TCP `remotePort` values; do not also bind
|
||||
them as `vhostHTTPPort`, because that collides with the DEV TCP proxy.
|
||||
- No secret values are stored in this repository. Operators must provide auth
|
||||
material out of band before a real deployment.
|
||||
- UniDesk backend, provider-gateway, and microservice proxy are not HWLAB runtime
|
||||
|
||||
@@ -2,12 +2,19 @@ serverAddr = "74.48.78.17"
|
||||
serverPort = 7000
|
||||
loginFailExit = true
|
||||
|
||||
[[proxies]]
|
||||
name = "hwlab-dev-cloud-web"
|
||||
type = "tcp"
|
||||
localIP = "hwlab-cloud-web.hwlab-dev.svc.cluster.local"
|
||||
localPort = 8080
|
||||
remotePort = 16666
|
||||
|
||||
[[proxies]]
|
||||
name = "hwlab-dev-edge-proxy"
|
||||
type = "tcp"
|
||||
localIP = "hwlab-edge-proxy.hwlab-dev.svc.cluster.local"
|
||||
localPort = 6667
|
||||
remotePort = 6667
|
||||
remotePort = 16667
|
||||
|
||||
[[proxies]]
|
||||
name = "hwlab-dev-tunnel-health"
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
bindPort = 7000
|
||||
|
||||
[[allowPorts]]
|
||||
start = 6667
|
||||
end = 6667
|
||||
start = 16666
|
||||
end = 16666
|
||||
|
||||
[[allowPorts]]
|
||||
start = 16667
|
||||
end = 16667
|
||||
|
||||
[[allowPorts]]
|
||||
start = 7402
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"cloud-api": "GET /health/live via hwlab-edge-proxy then hwlab-cloud-api:6667",
|
||||
"cloud-api-db": "DEV DB config gate requires HWLAB_CLOUD_DB_URL from Secret hwlab-cloud-api-dev-db/database-url plus HWLAB_CLOUD_DB_SSL_MODE=require; health reports only env presence and redacted Secret reference names, not live DB evidence",
|
||||
"cloud-web": "GET /health/live on hwlab-cloud-web:8080; consumes cloud-api only",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The master edge exposes only the DEV acceptance endpoint for this skeleton:
|
||||
|
||||
```text
|
||||
http://74.48.78.17:6667
|
||||
http://74.48.78.17:16667
|
||||
```
|
||||
|
||||
Health ownership:
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"environment": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"reverseLink": {
|
||||
"mode": "frp",
|
||||
"direction": "d601-to-master",
|
||||
"client": "hwlab-tunnel-client",
|
||||
"serverAddress": "74.48.78.17",
|
||||
"publicPort": 6667
|
||||
"publicPort": 16667,
|
||||
"webPublicPort": 16666
|
||||
},
|
||||
"contracts": [
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ metadata, then be copied into the catalog by the refresh command below.
|
||||
|
||||
- Environment: DEV only.
|
||||
- Namespace: `hwlab-dev`.
|
||||
- Public DEV endpoint: `http://74.48.78.17:6667`.
|
||||
- Public DEV endpoint: `http://74.48.78.17:16667`.
|
||||
- Profile: `dev`.
|
||||
- Health path: `/health/live`.
|
||||
- Artifact state: contract skeleton only.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"matrixVersion": "v1",
|
||||
"issue": "pikasTech/HWLAB#22",
|
||||
"environment": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"runtimeSubstitutePolicy": {
|
||||
"allowedExternalRoles": [
|
||||
"scheduling",
|
||||
@@ -40,7 +40,7 @@
|
||||
"id": "dev-endpoint",
|
||||
"label": "DEV endpoint",
|
||||
"serviceId": "hwlab-edge-proxy",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"role": "public-dev-ingress"
|
||||
},
|
||||
{
|
||||
@@ -78,7 +78,7 @@
|
||||
"serviceId": "hwlab-edge-proxy",
|
||||
"probe": {
|
||||
"method": "GET",
|
||||
"url": "http://74.48.78.17:6667/health"
|
||||
"url": "http://74.48.78.17:16667/health"
|
||||
},
|
||||
"requiredFields": [
|
||||
"serviceId",
|
||||
@@ -93,7 +93,7 @@
|
||||
"serviceId": "hwlab-edge-proxy",
|
||||
"probe": {
|
||||
"type": "route-observation",
|
||||
"target": "http://74.48.78.17:6667"
|
||||
"target": "http://74.48.78.17:16667"
|
||||
},
|
||||
"requiredFields": [
|
||||
"serviceId",
|
||||
@@ -343,14 +343,14 @@
|
||||
"id": "endpoint-freeze",
|
||||
"order": 2,
|
||||
"probe": "Compare documented and fixture DEV endpoints.",
|
||||
"successCriteria": "All DEV endpoints equal http://74.48.78.17:6667.",
|
||||
"successCriteria": "All DEV endpoints equal http://74.48.78.17:16667.",
|
||||
"failureCriteria": "Any alternate DEV endpoint or PROD target.",
|
||||
"blockerClass": "environment_blocker"
|
||||
},
|
||||
{
|
||||
"id": "dev-ingress-health",
|
||||
"order": 3,
|
||||
"probe": "Observe GET http://74.48.78.17:6667/health or recorded equivalent.",
|
||||
"probe": "Observe GET http://74.48.78.17:16667/health or recorded equivalent.",
|
||||
"successCriteria": "Request reaches HWLAB DEV route and returns HWLAB identity or accepted downstream health.",
|
||||
"failureCriteria": "Timeout, non-HWLAB target, or wrong port.",
|
||||
"blockerClass": "network_blocker"
|
||||
|
||||
@@ -6,9 +6,9 @@ DEV or PROD deployment.
|
||||
|
||||
## Fixed Boundary
|
||||
|
||||
- DEV endpoint: `http://74.48.78.17:6667`
|
||||
- DEV endpoint: `http://74.48.78.17:16667`
|
||||
- Deploy environment: `dev`
|
||||
- PROD endpoint `:6666` is reserved and must not be targeted by this matrix.
|
||||
- Legacy endpoint `:6666` is deprecated; browser entry is `:16666` and API/health entry is `:16667`.
|
||||
- Runtime route: client, master `hwlab-edge-proxy`, `frp`, D601
|
||||
`hwlab-dev/hwlab-router`, then HWLAB cloud/runtime services.
|
||||
- UniDesk backend, provider-gateway, and microservice proxy are not accepted as
|
||||
@@ -42,12 +42,12 @@ provide a current health timestamp. Existing skeleton services may expose
|
||||
|
||||
| Component | Service ID | Check | Success | Failure |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| DEV ingress | `hwlab-edge-proxy` | `GET http://74.48.78.17:6667/health` or routed service health | HTTP 2xx/3xx reaches HWLAB DEV path and reports edge identity or downstream HWLAB identity | Timeout, non-HWLAB response, PROD route, missing artifact fields |
|
||||
| master edge proxy | `hwlab-edge-proxy` | Edge proxy health and route table observation | Route for `http://74.48.78.17:6667` forwards to `frp` and identifies commit/image | No DEV route, wrong port, missing route observability |
|
||||
| DEV ingress | `hwlab-edge-proxy` | `GET http://74.48.78.17:16667/health` or routed service health | HTTP 2xx/3xx reaches HWLAB DEV path and reports edge identity or downstream HWLAB identity | Timeout, non-HWLAB response, PROD route, missing artifact fields |
|
||||
| master edge proxy | `hwlab-edge-proxy` | Edge proxy health and route table observation | Route for `http://74.48.78.17:16667` forwards to `frp` and identifies commit/image | No DEV route, wrong port, missing route observability |
|
||||
| frp tunnel | `hwlab-tunnel-client` | Tunnel session/status observation | Tunnel for DEV route is established to D601 router and identifies commit/image | Tunnel down, wrong target, ambiguous service identity |
|
||||
| D601 router | `hwlab-router` | `hwlab-dev/hwlab-router` health/status | Router is live, environment is `dev`, route target is cloud API/web or runtime service | Namespace mismatch, route missing, stale health timestamp |
|
||||
| Cloud API | `hwlab-cloud-api` | `GET /health`, `GET /live`, optional JSON-RPC `/rpc` probe | Returns service ID, `dev`, healthy/live status, redacted DB env readiness, and accepts a harmless protocol probe | HTTP error, unknown service ID, wrong environment, invalid JSON-RPC envelope, missing DB env readiness |
|
||||
| Cloud Web | `hwlab-cloud-web` | Static asset check and configured DEV endpoint | Web bundle references `http://74.48.78.17:6667` and does not call hardware directly | Endpoint drift, direct hardware control path, missing asset |
|
||||
| Cloud Web | `hwlab-cloud-web` | Static asset check and configured DEV endpoint | Web bundle references `http://74.48.78.17:16667` and does not call hardware directly | Endpoint drift, direct hardware control path, missing asset |
|
||||
| Agent manager | `hwlab-agent-mgr` | Agent scheduling health/status | Reports live manager, commit/image, and DEV environment; no worker leak after smoke | Scheduler unavailable, wrong environment, unbounded worker session |
|
||||
| Agent worker | `hwlab-agent-worker` | Worker health/status during dry-run or scoped smoke | Worker session is scoped to DEV project and emits trace/audit identifiers | Missing session identity, unsafe mutation, cleanup failure |
|
||||
| Agent skills | `hwlab-agent-skills` | Skill bundle version/status | Skill artifact is traceable to commit/build source and compatible with worker | Unversioned skill bundle, missing build source |
|
||||
@@ -67,7 +67,7 @@ listed above.
|
||||
| Step | Probe | Success Criteria | Failure Criteria | Blocker Class |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Confirm repository contract files and JSON checklist parse. | `docs/dev-acceptance-matrix.md` exists and `docs/dev-acceptance-checklist.json` parses. | Missing file or invalid JSON. | `contract_blocker` |
|
||||
| 2 | Verify frozen DEV endpoint. | Endpoint is exactly `http://74.48.78.17:6667` in docs/checklist/runtime fixtures. | Any alternate DEV endpoint or PROD target. | `environment_blocker` |
|
||||
| 2 | Verify frozen DEV endpoint. | Endpoint is exactly `http://74.48.78.17:16667` in docs/checklist/runtime fixtures. | Any alternate DEV endpoint or PROD target. | `environment_blocker` |
|
||||
| 3 | Observe DEV ingress health. | Request reaches HWLAB DEV route and returns HWLAB identity or accepted downstream health. | Timeout, non-HWLAB target, wrong port. | `network_blocker` |
|
||||
| 4 | Observe master edge proxy route. | Edge route maps DEV endpoint to `frp` and records artifact identity. | Missing route, stale route, missing artifact identity. | `network_blocker` |
|
||||
| 5 | Observe `frp` tunnel. | Tunnel links master edge to D601 router for DEV. | Tunnel down or target mismatch. | `network_blocker` |
|
||||
|
||||
@@ -98,7 +98,7 @@ The current `origin/main` deploy contract is blocked for real DEV apply:
|
||||
current source commit validated for this apply (`1e8d009e9531`).
|
||||
- The runner does not provide `kubectl`, so it cannot read or apply the D601
|
||||
`hwlab-dev` namespace.
|
||||
- The public DEV health probe at `http://74.48.78.17:6667/health/live` is not
|
||||
- The public DEV health probe at `http://74.48.78.17:16667/health/live` is not
|
||||
reachable from this runner.
|
||||
- `hwlab-cloud-api` now has a DEV DB env contract placeholder:
|
||||
`HWLAB_CLOUD_DB_URL` must come from Secret reference
|
||||
|
||||
@@ -12,7 +12,7 @@ HWLAB runtime.
|
||||
|
||||
## Health Evidence
|
||||
|
||||
`GET http://74.48.78.17:6667/health` must return HWLAB DEV JSON with:
|
||||
`GET http://74.48.78.17:16667/health` must return HWLAB DEV JSON with:
|
||||
|
||||
- `serviceId` for the HWLAB service answering the request;
|
||||
- `environment: "dev"`;
|
||||
@@ -44,9 +44,9 @@ present but no live DB probe is connected, `db.status` remains `"degraded"` and
|
||||
|
||||
The smoke classifies failures into these blockers:
|
||||
|
||||
- `frp_blocker`: public `:6667` is unavailable and frps control or tunnel
|
||||
- `frp_blocker`: public `:16667` is unavailable and frps control or tunnel
|
||||
health ports also refuse connections.
|
||||
- `edge_proxy_blocker`: frps is reachable, but public `:6667` refuses.
|
||||
- `edge_proxy_blocker`: frps is reachable, but public `:16667` refuses.
|
||||
- `k3s_service_blocker`: k3s is observable but HWLAB DEV services/endpoints are
|
||||
missing.
|
||||
- `dns_port_firewall_blocker`: public endpoint times out or cannot be resolved
|
||||
@@ -71,7 +71,7 @@ Blocked `frp_blocker` and `edge_proxy_blocker` reports now include a
|
||||
The committed report at `reports/dev-gate/dev-edge-health.json` records a
|
||||
read-only live run where:
|
||||
|
||||
- public `74.48.78.17:6667` returned `ECONNREFUSED`;
|
||||
- public `74.48.78.17:16667` returned `ECONNREFUSED`;
|
||||
- frps control `74.48.78.17:7000` returned `ECONNREFUSED`;
|
||||
- tunnel health `74.48.78.17:7402` returned `ECONNREFUSED`;
|
||||
- `kubectl` was not installed in this runner, so D601 k3s service state was not
|
||||
@@ -83,7 +83,7 @@ the public DEV route yet.
|
||||
|
||||
## DEV FRP Contract
|
||||
|
||||
`deploy/frp/frps.dev.toml` reserves public `6667` as a TCP `remotePort` for
|
||||
`hwlab-dev-edge-proxy`. Do not bind `vhostHTTPPort = 6667` in the same frps
|
||||
`deploy/frp/frps.dev.toml` reserves public `16666` for `hwlab-cloud-web` and
|
||||
public `16667` as a TCP `remotePort` for `hwlab-dev-edge-proxy`. Do not bind `vhostHTTPPort = 16667` in the same frps
|
||||
config; that collides with the TCP proxy and can keep the DEV route from
|
||||
registering cleanly.
|
||||
|
||||
@@ -83,7 +83,7 @@ The JSON output groups recommended next actions in dependency order:
|
||||
4. `cloud-api-db`: prove DEV cloud-api DB env and health readiness with
|
||||
redacted values only.
|
||||
5. `dev-edge`, `dev-edge-health`, `dev-ingress-health`: repair
|
||||
`:6667`/frp/edge/router reachability, then rerun read-only DEV edge health
|
||||
`:16667`/frp/edge/router reachability, then rerun read-only DEV edge health
|
||||
before M3/M4/M5 live loops.
|
||||
|
||||
## Boundaries
|
||||
|
||||
@@ -61,7 +61,7 @@ The preflight checks:
|
||||
- The runner can perform read-only `kubectl` probes against `hwlab-dev`.
|
||||
- `reports/dev-gate/dev-edge-health.json`, when present, records read-only
|
||||
public edge, frps, tunnel-health, and k3s observability evidence.
|
||||
- `http://74.48.78.17:6667/health/live` responds.
|
||||
- `http://74.48.78.17:16667/health/live` responds.
|
||||
- GHCR manifests are visible without reading credentials.
|
||||
- Deploy images do not point at UniDesk, provider-gateway, or microservice-proxy
|
||||
substitutes.
|
||||
|
||||
@@ -8,7 +8,7 @@ This document records the DEV-only M4 agent loop smoke for
|
||||
- Verify the agent manager, worker workspace, explicit skills commit
|
||||
injection, trace/evidence chain, and cleanup behavior.
|
||||
- Keep the runtime path on the fixed DEV boundary at
|
||||
`http://74.48.78.17:6667`.
|
||||
`http://74.48.78.17:16667`.
|
||||
- Avoid secret reads, PROD access, long-running agent tasks, and direct gateway
|
||||
access.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ the referenced #7/#11/#12/#16 guardrails.
|
||||
|
||||
Applied constraints:
|
||||
|
||||
- MVP acceptance is DEV only at `http://74.48.78.17:6667`.
|
||||
- MVP acceptance is DEV only at `http://74.48.78.17:16667`.
|
||||
- PROD is reserved and must not be deployed for this audit.
|
||||
- HWLAB runtime services must keep their frozen service IDs and must not be
|
||||
replaced by UniDesk services.
|
||||
|
||||
@@ -38,7 +38,7 @@ npm run m1:smoke
|
||||
This smoke is not the future DEV e2e acceptance test. It intentionally does not:
|
||||
|
||||
- deploy to DEV or PROD;
|
||||
- call `http://74.48.78.17:6667`;
|
||||
- call `http://74.48.78.17:16667`;
|
||||
- run browser e2e;
|
||||
- read secrets, tokens, kubeconfig, tunnel credentials, or deploy manifests;
|
||||
- validate real gateway, router, frp, edge proxy, agent, worker, or hardware
|
||||
|
||||
@@ -43,7 +43,7 @@ node scripts/m2-dev-deploy-smoke.mjs --live --confirm-dev --confirmed-non-produc
|
||||
|
||||
Before using `--live`, confirm all of the following:
|
||||
|
||||
- the target remains `http://74.48.78.17:6667`;
|
||||
- the target remains `http://74.48.78.17:16667`;
|
||||
- the workspace is not a PROD or shared deployment context;
|
||||
- no prohibited action is being attempted;
|
||||
- the request is observational only and does not mutate runtime state.
|
||||
|
||||
@@ -79,7 +79,7 @@ node scripts/dev-m3-hardware-loop-smoke.mjs --live --confirm-dev --confirmed-non
|
||||
```
|
||||
|
||||
The smoke writes `reports/dev-gate/dev-m3-hardware-loop.json`. It first checks
|
||||
the frozen DEV endpoint, `http://74.48.78.17:6667`, and stops at the first
|
||||
the frozen DEV endpoint, `http://74.48.78.17:16667`, and stops at the first
|
||||
critical blocker from `docs/dev-acceptance-matrix.md`. When DEV ingress is not
|
||||
observable, the report is `blocked` and the script does not run the live
|
||||
`do.write true -> di.read true` operation.
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ Do not switch to the real command until all of these are true:
|
||||
|
||||
1. `node scripts/m5-mvp-e2e-dry-run.mjs` passes on `origin/main`.
|
||||
2. `docs/dev-acceptance-checklist.json` and `docs/dev-acceptance-matrix.md`
|
||||
still freeze DEV at `http://74.48.78.17:6667`.
|
||||
still freeze DEV at `http://74.48.78.17:16667`.
|
||||
3. Every accepted artifact has `serviceId`, `commitId`, `image`, `tag`,
|
||||
`digest` or `not_applicable` reason, `buildSource`, `deployEnv`, and
|
||||
`healthTimestamp`.
|
||||
|
||||
@@ -12,8 +12,8 @@ heavyweight e2e run by itself.
|
||||
|
||||
- Repository: `pikasTech/HWLAB`.
|
||||
- MVP environment: `dev`.
|
||||
- DEV endpoint: `http://74.48.78.17:6667`.
|
||||
- PROD endpoint `:6666` is reserved and is not an MVP acceptance target.
|
||||
- DEV endpoint: `http://74.48.78.17:16667`.
|
||||
- Legacy endpoint `:6666` is deprecated and is not an MVP acceptance target; the browser entry is `:16666`.
|
||||
- Runtime route: client, master `hwlab-edge-proxy`, `frp`, D601
|
||||
`hwlab-dev/hwlab-router`, then HWLAB cloud/runtime services.
|
||||
- HWLAB runtime services must keep the frozen service IDs in `README.md`.
|
||||
@@ -89,7 +89,7 @@ Current unblock order:
|
||||
approved kubeconfig/maintenance bridge, without reading Secrets.
|
||||
4. Prove DEV `hwlab-cloud-api` DB env and health readiness with redacted values
|
||||
only.
|
||||
5. Repair the `:6667`/`frp`/edge/router path and rerun read-only DEV edge
|
||||
5. Repair the `:16667`/`frp`/edge/router path and rerun read-only DEV edge
|
||||
health before any M3, M4, or M5 live loop.
|
||||
|
||||
## Phase Gates
|
||||
@@ -102,7 +102,7 @@ reason.
|
||||
| --- | --- | --- | --- |
|
||||
| Source-only | M0 contract, M2/M4/M5 source readiness | Worktree is based on latest `origin/main`; no secret or deployment access is needed; target files are docs, protocol, schema, fixtures, scripts, or manifests only. | Contract files parse; frozen endpoint and service IDs are unchanged; `npm run check` or the narrow validation command passes; no DEV/PROD mutation occurred. |
|
||||
| Local smoke | M1 plus local M3/M4 contract smoke | Source-only has passed; commands use localhost or fixture data only; ports are ephemeral; no DEV endpoint call is needed. | Smoke commands pass; generated output states local or dry-run boundary; no real hardware, tunnel, kube, or deploy action occurred. |
|
||||
| Dry-run | M2/M4/M5 readiness without runtime mutation | Source-only and local smoke pass; CLI/runtime fixtures identify `dev` and `http://74.48.78.17:6667`; operator confirms command includes `--dry-run` where applicable. | Dry-run output names MVP route, hardware trusted closed loop, agent automation closed loop, evidence records, and cleanup plan; output states no DEV/PROD changes were made. |
|
||||
| Dry-run | M2/M4/M5 readiness without runtime mutation | Source-only and local smoke pass; CLI/runtime fixtures identify `dev` and `http://74.48.78.17:16667`; operator confirms command includes `--dry-run` where applicable. | Dry-run output names MVP route, hardware trusted closed loop, agent automation closed loop, evidence records, and cleanup plan; output states no DEV/PROD changes were made. |
|
||||
| DEV runtime smoke | M2 DEV runtime observation | M0-M1 and required dry-runs pass; commander explicitly authorizes a read-only DEV smoke window; expected HWLAB artifacts and rollback owner are known; no secret reads are required. | Read-only health/status observations match `docs/dev-acceptance-matrix.md`; every observed service is HWLAB, in `dev`, and traceable to commit/image/tag/digest or a documented `not_applicable` reason. |
|
||||
| MVP e2e | M3/M4/M5 DEV-only MVP acceptance | DEV runtime smoke has passed; commander explicitly authorizes a narrow DEV-only MVP smoke; project/gateway/agent/hardware scope is named; cleanup owner and stop criteria are recorded. | One bounded DEV-only flow completes: project, gateway session, resources, patch-panel wiring, agent and worker sessions, hardware operation, trace events, audit events, evidence record, and cleanup. No PROD or heavyweight e2e action occurred. |
|
||||
|
||||
@@ -169,7 +169,7 @@ Entry:
|
||||
|
||||
Exit:
|
||||
|
||||
- Frozen DEV endpoint remains `http://74.48.78.17:6667`.
|
||||
- Frozen DEV endpoint remains `http://74.48.78.17:16667`.
|
||||
- PROD is not introduced as an acceptance target.
|
||||
- Frozen service IDs remain HWLAB service IDs.
|
||||
- JSON, schema, script syntax, and documented contract checks pass.
|
||||
@@ -184,7 +184,7 @@ Entry:
|
||||
- Source-only gate passed.
|
||||
- Commands are known local checks such as `npm run m1:smoke`,
|
||||
`node scripts/m3-hardware-loop-smoke.mjs`, or targeted `node --test`.
|
||||
- No command calls `http://74.48.78.17:6667`, `:6666`, kube, `frp`, edge
|
||||
- No command calls `http://74.48.78.17:16667`, `:16666`, kube, `frp`, edge
|
||||
runtime, or real hardware.
|
||||
|
||||
Exit:
|
||||
|
||||
@@ -13,7 +13,7 @@ UniDesk or HWLAB runtime process.
|
||||
|
||||
- HWLAB runtime service identity is the frozen `hwlab-*` service ID set.
|
||||
- DEV is the only accepted runtime environment for this guard.
|
||||
- The fixed DEV endpoint is `http://74.48.78.17:6667`.
|
||||
- The fixed DEV endpoint is `http://74.48.78.17:16667`.
|
||||
- The DEV namespace is `hwlab-dev`.
|
||||
- PROD deployment and PROD acceptance remain disabled.
|
||||
- UniDesk backend, provider-gateway, and microservice proxy are forbidden
|
||||
|
||||
@@ -317,13 +317,13 @@ export function createSkillsManifest({
|
||||
commitId,
|
||||
version,
|
||||
namespace: "hwlab",
|
||||
endpoint: "http://74.48.78.17:6667",
|
||||
endpoint: "http://74.48.78.17:16667",
|
||||
profiles: {
|
||||
dev: {
|
||||
name: "dev",
|
||||
enabled: true,
|
||||
namespace: "hwlab-dev",
|
||||
endpoint: "http://74.48.78.17:6667",
|
||||
endpoint: "http://74.48.78.17:16667",
|
||||
notes: "explicit repo-local skills artifact"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const DEV_ENDPOINT = "http://74.48.78.17:6667";
|
||||
export const DEV_ENDPOINT = "http://74.48.78.17:16667";
|
||||
export const DEFAULT_GATE_REPORT_PATH = "reports/dev-gate/dev-mvp-gate-report.json";
|
||||
export const DEFAULT_M5_PLAN_PATH = "fixtures/mvp/m5-e2e/dry-run-plan.json";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const ENVIRONMENT_DEV = "dev";
|
||||
export const DEV_ENDPOINT = "http://74.48.78.17:6667";
|
||||
export const DEV_ENDPOINT = "http://74.48.78.17:16667";
|
||||
export const JSON_RPC_VERSION = "2.0";
|
||||
|
||||
export const SERVICE_IDS = Object.freeze([
|
||||
|
||||
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const fixedNow = () => "2026-05-21T00:00:00.000Z";
|
||||
const DEV_ENDPOINT = "http://74.48.78.17:6667";
|
||||
const DEV_ENDPOINT = "http://74.48.78.17:16667";
|
||||
const requiredDocs = ["docs/dev-acceptance-matrix.md", "docs/dev-m4-agent-loop.md"];
|
||||
const reportPath = "reports/dev-gate/dev-m4-agent-loop.json";
|
||||
const preflightCommand = "node scripts/dev-m4-agent-loop-smoke.mjs --live --confirm-dev --confirmed-non-production";
|
||||
@@ -171,7 +171,7 @@ function buildReport(fixture, evidence, result) {
|
||||
devPreconditions: {
|
||||
status: statusForResult(result),
|
||||
requirements: [
|
||||
"DEV route remains frozen at http://74.48.78.17:6667",
|
||||
"DEV route remains frozen at http://74.48.78.17:16667",
|
||||
"No secret reads, real deployment, or long-running agent task is attempted",
|
||||
"Agent manager, worker, and skills are reachable only through the cloud API boundary"
|
||||
],
|
||||
|
||||
@@ -28,7 +28,7 @@ async function captureCli(args) {
|
||||
}
|
||||
|
||||
const summary = loadMvpGateSummary(repoRoot);
|
||||
assert.equal(summary.endpoint, "http://74.48.78.17:6667");
|
||||
assert.equal(summary.endpoint, "http://74.48.78.17:16667");
|
||||
assert.deepEqual(
|
||||
summary.milestones.map((item) => item.id),
|
||||
["M0", "M1", "M2", "M3", "M4", "M5"]
|
||||
|
||||
@@ -149,7 +149,7 @@ function expectSkillsManifest(manifest, fixture) {
|
||||
assert.equal(manifest.commitId, fixture.skillCommitId);
|
||||
assert.equal(manifest.version, fixture.skillVersion);
|
||||
assert.equal(manifest.namespace, "hwlab");
|
||||
assert.equal(manifest.endpoint, "http://74.48.78.17:6667");
|
||||
assert.equal(manifest.endpoint, "http://74.48.78.17:16667");
|
||||
assert.equal(manifest.profiles.dev.enabled, true);
|
||||
assert.equal(manifest.profiles.dev.namespace, "hwlab-dev");
|
||||
assert.equal(manifest.services[0].serviceId, AGENT_SKILLS_SERVICE_ID);
|
||||
|
||||
@@ -12,7 +12,7 @@ const matrixPath = "docs/dev-acceptance-matrix.md";
|
||||
const runtimePath = "fixtures/mvp/runtime.json";
|
||||
const contractPath = "protocol/mvp-e2e-contract.md";
|
||||
const commonSchemaPath = "protocol/schemas/common.json";
|
||||
const DEV_ENDPOINT = "http://74.48.78.17:6667";
|
||||
const DEV_ENDPOINT = "http://74.48.78.17:16667";
|
||||
const ENVIRONMENT_DEV = "dev";
|
||||
const JSON_RPC_VERSION = "2.0";
|
||||
let serviceIds = [];
|
||||
@@ -80,7 +80,7 @@ function assertArrayIncludesAll(label, actual, expected) {
|
||||
|
||||
function assertNoProdTarget(value, trail = "plan") {
|
||||
if (typeof value === "string") {
|
||||
assert.equal(value.includes(":6666"), false, `${trail} must not target PROD port`);
|
||||
assert.equal(value.includes(":6666"), false, `${trail} must not target deprecated legacy port`);
|
||||
assert.equal(/\bprod\b/i.test(value), false, `${trail} must not target PROD`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ function refreshDocuments({ deploy, catalog, workloads, target, publishRecords,
|
||||
const service = catalogByService.get(serviceId);
|
||||
if (!service) continue;
|
||||
container.image = service.image;
|
||||
updateEnvList(container.env, service, target, publishRecords?.get(serviceId) ?? null);
|
||||
updateEnvList(container.env, service, target, records?.get(serviceId) ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { constants as fsConstants } from "node:fs";
|
||||
import { access, readFile, writeFile } from "node:fs/promises";
|
||||
import { request as httpRequest } from "node:http";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
import {
|
||||
@@ -470,30 +470,40 @@ async function checkCloudApiDb(deploy, workloads, blockers) {
|
||||
);
|
||||
}
|
||||
|
||||
let health = null;
|
||||
try {
|
||||
const modulePath = pathToFileURL(path.join(repoRoot, "internal/cloud/server.mjs")).href;
|
||||
const { buildHealthPayload } = await import(modulePath);
|
||||
health = buildHealthPayload();
|
||||
if (health.db?.ready !== true) {
|
||||
addBlocker(
|
||||
blockers,
|
||||
"runtime_blocker",
|
||||
"cloud-api-db",
|
||||
`cloud-api health reports DB config blocked; missing ${health.db?.missingEnv?.join(", ") ?? "unknown DB env"}`
|
||||
);
|
||||
const configReady = missingManifest.length === 0 && missingK8s.length === 0 && missingSecretRefs.length === 0;
|
||||
const health = {
|
||||
status: configReady ? "degraded" : "blocked",
|
||||
ready: false,
|
||||
configReady,
|
||||
connected: false,
|
||||
connectionChecked: false,
|
||||
fields: requiredEnv.map((env) => ({
|
||||
name: env.name,
|
||||
present: env.manifestPresent && env.k8sPresent && (!env.secretRef || env.secretRef.present),
|
||||
redacted: env.redacted
|
||||
})),
|
||||
missingEnv: uniqueStrings([...missingManifest, ...missingK8s]),
|
||||
secretRefs: requiredEnv
|
||||
.filter((env) => env.secretRef)
|
||||
.map((env) => ({
|
||||
env: env.name,
|
||||
secretName: env.secretRef.secretName,
|
||||
secretKey: env.secretRef.secretKey,
|
||||
present: env.secretRef.present,
|
||||
redacted: true
|
||||
})),
|
||||
safety: {
|
||||
liveDbEvidence: false
|
||||
}
|
||||
} catch (error) {
|
||||
addBlocker(blockers, "observability_blocker", "cloud-api-health", `Cannot inspect cloud-api health payload: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
status: missingManifest.length === 0 && missingK8s.length === 0 && missingSecretRefs.length === 0 ? "contract-ready" : "contract-blocked",
|
||||
status: configReady ? "contract-ready" : "contract-blocked",
|
||||
requiredEnv,
|
||||
missingManifest,
|
||||
missingK8s,
|
||||
missingSecretRefs,
|
||||
runtimeHealth: health?.db ? summarizeDbContract(health.db) : null,
|
||||
runtimeHealth: summarizeDbContract(health),
|
||||
secretMaterialRead: false,
|
||||
valuesRedacted: true,
|
||||
liveDbEvidence: false,
|
||||
|
||||
@@ -11,9 +11,9 @@ import { DEV_ENDPOINT, ENVIRONMENT_DEV } from "../../internal/protocol/index.mjs
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const defaultReportPath = path.join(repoRoot, "reports/dev-gate/dev-edge-health.json");
|
||||
const publicHost = "74.48.78.17";
|
||||
const publicPort = 6667;
|
||||
const publicPort = 16667;
|
||||
const namespace = "hwlab-dev";
|
||||
const tcpPorts = [6667, 7000, 7402];
|
||||
const tcpPorts = [16667, 7000, 7402];
|
||||
const runtimeServices = [
|
||||
{ serviceId: "hwlab-cloud-api", port: 6667 },
|
||||
{ serviceId: "hwlab-router", port: 7401 },
|
||||
@@ -161,7 +161,7 @@ async function createDevGateReport(edgeHealth) {
|
||||
devPreconditions: {
|
||||
status,
|
||||
requirements: [
|
||||
"http://74.48.78.17:6667/health returns HWLAB DEV JSON with service, commit, and image evidence",
|
||||
"http://74.48.78.17:16667/health returns HWLAB DEV JSON with service, commit, and image evidence",
|
||||
"frps control on 74.48.78.17:7000 accepts the D601 frp client",
|
||||
"hwlab-dev hwlab-edge-proxy, hwlab-tunnel-client, hwlab-router, and hwlab-cloud-api are observable",
|
||||
"No PROD endpoint, secret read, restart, or UniDesk runtime substitute is used"
|
||||
@@ -212,7 +212,7 @@ async function inspectContracts() {
|
||||
const frps = await readText("deploy/frp/frps.dev.toml");
|
||||
const deploy = await readJson("deploy/deploy.json");
|
||||
const master = await readJson("deploy/master-edge/health-contract.json");
|
||||
const frpsUsesVhost6667 = /vhostHTTPPort\s*=\s*6667/u.test(frps);
|
||||
const frpsUsesVhostEdgePort = /vhostHTTPPort\s*=\s*(16667|6667)/u.test(frps);
|
||||
|
||||
return {
|
||||
devEndpoint: DEV_ENDPOINT,
|
||||
@@ -220,10 +220,15 @@ async function inspectContracts() {
|
||||
frpcTargetsEdgeProxy:
|
||||
/localIP\s*=\s*"hwlab-edge-proxy\.hwlab-dev\.svc\.cluster\.local"/u.test(frpc) &&
|
||||
/localPort\s*=\s*6667/u.test(frpc) &&
|
||||
/remotePort\s*=\s*6667/u.test(frpc),
|
||||
frpsTcpRemotePort6667: /start\s*=\s*6667[\s\S]*end\s*=\s*6667/u.test(frps),
|
||||
frpsUsesVhost6667,
|
||||
configRisk: frpsUsesVhost6667 ? "port_conflict" : "none"
|
||||
/remotePort\s*=\s*16667/u.test(frpc),
|
||||
frpcTargetsCloudWeb:
|
||||
/localIP\s*=\s*"hwlab-cloud-web\.hwlab-dev\.svc\.cluster\.local"/u.test(frpc) &&
|
||||
/localPort\s*=\s*8080/u.test(frpc) &&
|
||||
/remotePort\s*=\s*16666/u.test(frpc),
|
||||
frpsTcpRemotePort16667: /start\s*=\s*16667[\s\S]*end\s*=\s*16667/u.test(frps),
|
||||
frpsTcpRemotePort16666: /start\s*=\s*16666[\s\S]*end\s*=\s*16666/u.test(frps),
|
||||
frpsUsesVhostEdgePort,
|
||||
configRisk: frpsUsesVhostEdgePort ? "port_conflict" : "none"
|
||||
},
|
||||
masterEdge: {
|
||||
endpoint: master.endpoint,
|
||||
@@ -375,7 +380,7 @@ async function inspectClusterDns() {
|
||||
}
|
||||
|
||||
function classify(report) {
|
||||
const port6667 = report.publicTcp.find((probe) => probe.port === publicPort);
|
||||
const publicEdgePort = report.publicTcp.find((probe) => probe.port === publicPort);
|
||||
const frpsControl = report.publicTcp.find((probe) => probe.port === 7000);
|
||||
const tunnelHealth = report.publicTcp.find((probe) => probe.port === 7402);
|
||||
const publicHealth = report.publicHttp.find((probe) => probe.url.endsWith("/health"));
|
||||
@@ -383,11 +388,11 @@ function classify(report) {
|
||||
if (publicHealth?.ok && publicHealth.json?.serviceId && publicHealth.json?.environment === ENVIRONMENT_DEV) {
|
||||
return classifyReachableHealth(publicHealth.json);
|
||||
}
|
||||
if (port6667?.status === "error" && port6667.code === "ECONNREFUSED") {
|
||||
return classifyRefusedPort(port6667, frpsControl, tunnelHealth);
|
||||
if (publicEdgePort?.status === "error" && publicEdgePort.code === "ECONNREFUSED") {
|
||||
return classifyRefusedPort(publicEdgePort, frpsControl, tunnelHealth);
|
||||
}
|
||||
if (port6667?.status === "timeout") {
|
||||
return blocker("dns_port_firewall_blocker", "public 6667 timed out before any HTTP response");
|
||||
if (publicEdgePort?.status === "timeout") {
|
||||
return blocker("dns_port_firewall_blocker", "public 16667 timed out before any HTTP response");
|
||||
}
|
||||
if (publicHealth && !publicHealth.ok && publicHealth.status) {
|
||||
return blocker("app_health_blocker", `public /health returned HTTP ${publicHealth.status}`);
|
||||
@@ -409,17 +414,17 @@ function classifyReachableHealth(json) {
|
||||
return { status: "pass", classification: "none", blocker: null };
|
||||
}
|
||||
|
||||
function classifyRefusedPort(port6667, frpsControl, tunnelHealth) {
|
||||
function classifyRefusedPort(publicEdgePort, frpsControl, tunnelHealth) {
|
||||
if (frpsControl?.status === "error" && frpsControl.code === "ECONNREFUSED") {
|
||||
const allPortsRefused = tunnelHealth?.status === "error" && tunnelHealth.code === "ECONNREFUSED";
|
||||
const diagnosis = {
|
||||
likelyLayer: "master-edge/frps",
|
||||
confidence: "high",
|
||||
likelyCause: allPortsRefused
|
||||
? "public 6667, frps control 7000, and tunnel health 7402 all refuse TCP connections; the master-edge frps listener or its ports are most likely down or closed"
|
||||
: "public 6667 and frps control 7000 both refuse TCP connections; the master-edge frps listener or its ports are most likely down or closed",
|
||||
? "public 16667, frps control 7000, and tunnel health 7402 all refuse TCP connections; the master-edge frps listener or its ports are most likely down or closed"
|
||||
: "public 16667 and frps control 7000 both refuse TCP connections; the master-edge frps listener or its ports are most likely down or closed",
|
||||
evidence: [
|
||||
summarizeTcpProbe(port6667),
|
||||
summarizeTcpProbe(publicEdgePort),
|
||||
summarizeTcpProbe(frpsControl),
|
||||
summarizeTcpProbe(tunnelHealth)
|
||||
],
|
||||
@@ -433,12 +438,12 @@ function classifyRefusedPort(port6667, frpsControl, tunnelHealth) {
|
||||
return blocker("frp_blocker", diagnosis.likelyCause, diagnosis);
|
||||
}
|
||||
if (tunnelHealth?.status === "error" && tunnelHealth.code === "ECONNREFUSED") {
|
||||
return blocker("frp_blocker", "public 6667 refuses TCP and tunnel health 7402 is not reachable", {
|
||||
return blocker("frp_blocker", "public 16667 refuses TCP and tunnel health 7402 is not reachable", {
|
||||
likelyLayer: "d601/frpc",
|
||||
confidence: "medium",
|
||||
likelyCause: "public 6667 and tunnel health 7402 both refuse TCP connections; the D601 frp client or its remote port is the likely break",
|
||||
likelyCause: "public 16667 and tunnel health 7402 both refuse TCP connections; the D601 frp client or its remote port is the likely break",
|
||||
evidence: [
|
||||
summarizeTcpProbe(port6667),
|
||||
summarizeTcpProbe(publicEdgePort),
|
||||
summarizeTcpProbe(tunnelHealth)
|
||||
],
|
||||
notProven: [
|
||||
@@ -448,12 +453,12 @@ function classifyRefusedPort(port6667, frpsControl, tunnelHealth) {
|
||||
nextTask: "Restore the D601 tunnel client or its remote port, then rerun the read-only edge smoke."
|
||||
});
|
||||
}
|
||||
return blocker("edge_proxy_blocker", "public 6667 refuses TCP connections", {
|
||||
return blocker("edge_proxy_blocker", "public 16667 refuses TCP connections", {
|
||||
likelyLayer: "master-edge/edge-proxy",
|
||||
confidence: "medium",
|
||||
likelyCause: "public 6667 refuses TCP while frps control 7000 stays reachable, so the public edge proxy or its listener is the likely break",
|
||||
likelyCause: "public 16667 refuses TCP while frps control 7000 stays reachable, so the public edge proxy or its listener is the likely break",
|
||||
evidence: [
|
||||
summarizeTcpProbe(port6667),
|
||||
summarizeTcpProbe(publicEdgePort),
|
||||
summarizeTcpProbe(frpsControl)
|
||||
],
|
||||
notProven: [
|
||||
|
||||
@@ -245,7 +245,7 @@ function applyPriority(blocker) {
|
||||
priority: "P2",
|
||||
unblockOrder: 6,
|
||||
unblocks: [issue(36), issue(38), issue(37), issue(39)],
|
||||
rationale: "The :6667/frp path must be reachable before any DEV-LIVE M3, M4, or M5 evidence can be collected."
|
||||
rationale: "The :16667/frp path must be reachable before any DEV-LIVE M3, M4, or M5 evidence can be collected."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ function buildDoD(reports, milestones, blockers) {
|
||||
id: "dev-edge-frp-6667",
|
||||
status: hasBlocker(blockers, (blocker) => blocker.scope.includes("edge") || blocker.scope.includes("ingress") || blocker.summary.includes("6667")) ? "blocked" : "pass",
|
||||
evidenceLevel: "BLOCKED",
|
||||
summary: "No committed report proves live HTTP 200/JSON on http://74.48.78.17:6667."
|
||||
summary: "No committed report proves live HTTP 200/JSON on http://74.48.78.17:16667."
|
||||
},
|
||||
{
|
||||
id: "cloud-api-db-ready",
|
||||
@@ -673,7 +673,7 @@ function evidenceRequiredFor(scope) {
|
||||
if (scope.includes("artifact") || scope === "ghcr") return "Artifact publish report with ciPublished=true, registryVerified=true, and sha256 digest for each frozen service ID.";
|
||||
if (scope.includes("kubectl") || scope.includes("k3s")) return "Read-only kubectl/k3s report proving pods/services/configmaps are observable in hwlab-dev without reading Secrets.";
|
||||
if (scope.includes("cloud-api-db")) return "Cloud API health/live output showing DB env ready and redacted secret references, without secret material.";
|
||||
if (scope.includes("edge") || scope.includes("ingress")) return "Read-only DEV route observation for :6667/frp/edge/router with HWLAB service identity and artifact identity.";
|
||||
if (scope.includes("edge") || scope.includes("ingress")) return "Read-only DEV route observation for :16667/frp/edge/router with HWLAB service identity and artifact identity.";
|
||||
return "A committed report with the exact evidence level and command used.";
|
||||
}
|
||||
|
||||
|
||||
@@ -351,9 +351,11 @@ function assertFrpStatic(frpc, frps, masterEdge) {
|
||||
assert.equal(frpc.serverAddr, "74.48.78.17");
|
||||
assert.equal(frpc.serverPort, 7000);
|
||||
assert.equal(frps.bindPort, 7000);
|
||||
assert.equal(Object.hasOwn(frps, "vhostHTTPPort"), false, "frps must not bind vhostHTTPPort because 6667 is reserved for the TCP proxy");
|
||||
assert.ok(frpc.proxies.some((proxy) => proxy.name === "hwlab-dev-edge-proxy" && proxy.remotePort === 6667));
|
||||
assert.ok(frps.allowPorts.some((port) => port.start === 6667 && port.end === 6667));
|
||||
assert.equal(Object.hasOwn(frps, "vhostHTTPPort"), false, "frps must not bind vhostHTTPPort because 16667 is reserved for the TCP proxy");
|
||||
assert.ok(frpc.proxies.some((proxy) => proxy.name === "hwlab-dev-edge-proxy" && proxy.remotePort === 16667));
|
||||
assert.ok(frpc.proxies.some((proxy) => proxy.name === "hwlab-dev-cloud-web" && proxy.remotePort === 16666));
|
||||
assert.ok(frps.allowPorts.some((port) => port.start === 16667 && port.end === 16667));
|
||||
assert.ok(frps.allowPorts.some((port) => port.start === 16666 && port.end === 16666));
|
||||
assert.equal(masterEdge.environment, ENVIRONMENT_DEV);
|
||||
assert.equal(masterEdge.endpoint, DEV_ENDPOINT);
|
||||
assert.equal(masterEdge.reverseLink.mode, "frp");
|
||||
@@ -731,7 +733,7 @@ async function validateEdgeContracts(reporter, masterEdge) {
|
||||
parseToml("deploy/frp/frps.dev.toml")
|
||||
]);
|
||||
assertFrpStatic(frpc, frps, masterEdge);
|
||||
reporter.check("frp-master-edge-static", "edge", "pass", "FRP and master-edge contracts describe D601-to-master DEV on port 6667.");
|
||||
reporter.check("frp-master-edge-static", "edge", "pass", "FRP and master-edge contracts describe D601-to-master DEV on public ports 16666/16667.");
|
||||
} catch (error) {
|
||||
reporter.check("frp-master-edge-static", "edge", "blocked", error.message);
|
||||
reporter.block({
|
||||
|
||||
@@ -66,7 +66,7 @@ function assertCommonSchema(commonSchema) {
|
||||
function assertDeploySchema(deploySchema) {
|
||||
const serviceIds = deploySchema.$defs.service.properties.serviceId.enum;
|
||||
assert.deepEqual(serviceIds, SERVICE_IDS, "deploy service ids must match runtime constants");
|
||||
assert.equal(deploySchema.properties.endpoint.const, "http://74.48.78.17:6667");
|
||||
assert.equal(deploySchema.properties.endpoint.const, "http://74.48.78.17:16667");
|
||||
}
|
||||
|
||||
function assertEnvelopeValidation() {
|
||||
|
||||
@@ -592,7 +592,7 @@ function assertDevDeployApplyReport(report, label) {
|
||||
assertObject(plan.target, `${label}.devDeployApply.target`);
|
||||
assert.equal(plan.target.environment, "dev", `${label}.devDeployApply.target.environment`);
|
||||
assert.equal(plan.target.namespace, "hwlab-dev", `${label}.devDeployApply.target.namespace`);
|
||||
assert.equal(plan.target.endpoint, "http://74.48.78.17:6667", `${label}.devDeployApply.target.endpoint`);
|
||||
assert.equal(plan.target.endpoint, "http://74.48.78.17:16667", `${label}.devDeployApply.target.endpoint`);
|
||||
assert.equal(plan.target.prodDisabled, true, `${label}.devDeployApply.target.prodDisabled`);
|
||||
|
||||
assertObject(plan.artifactPlan, `${label}.devDeployApply.artifactPlan`);
|
||||
@@ -817,7 +817,7 @@ async function validateDevEdgeReport(report, label) {
|
||||
|
||||
assertObject(report.edgeHealth, `${label}.edgeHealth`);
|
||||
assertTimestamp(report.edgeHealth.generatedAt, `${label}.edgeHealth.generatedAt`);
|
||||
assert.equal(report.edgeHealth.endpoint, "http://74.48.78.17:6667", `${label}.edgeHealth.endpoint`);
|
||||
assert.equal(report.edgeHealth.endpoint, "http://74.48.78.17:16667", `${label}.edgeHealth.endpoint`);
|
||||
assert.ok(
|
||||
["pass", "blocker", "not_run"].includes(report.edgeHealth.status),
|
||||
`${label}.edgeHealth.status must be pass, blocker, or not_run`
|
||||
@@ -891,7 +891,7 @@ async function validateDevM3Report(report, label) {
|
||||
}
|
||||
|
||||
assertObject(report.runtimeTarget, `${label}.runtimeTarget`);
|
||||
assert.equal(report.runtimeTarget.endpoint, "http://74.48.78.17:6667", `${label}.runtimeTarget.endpoint`);
|
||||
assert.equal(report.runtimeTarget.endpoint, "http://74.48.78.17:16667", `${label}.runtimeTarget.endpoint`);
|
||||
assert.equal(report.runtimeTarget.namespace, "hwlab-dev", `${label}.runtimeTarget.namespace`);
|
||||
assert.equal(report.runtimeTarget.environment, "dev", `${label}.runtimeTarget.environment`);
|
||||
assert.equal(report.runtimeTarget.requiredBoxSimulators, 2, `${label}.runtimeTarget.requiredBoxSimulators`);
|
||||
@@ -1153,7 +1153,7 @@ async function validateAggregatorV2Report(relativePath, report) {
|
||||
assertTimestamp(report.generatedAt, `${label}.generatedAt`);
|
||||
assert.match(report.generatedFromCommit, /^([a-f0-9]{7,40}|unknown)$/, `${label}.generatedFromCommit`);
|
||||
assert.equal(report.environment, "dev", `${label}.environment`);
|
||||
assert.equal(report.endpoint, "http://74.48.78.17:6667", `${label}.endpoint`);
|
||||
assert.equal(report.endpoint, "http://74.48.78.17:16667", `${label}.endpoint`);
|
||||
assert.equal(report.devOnly, true, `${label}.devOnly`);
|
||||
assert.equal(report.prodDisabled, true, `${label}.prodDisabled`);
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ function assertMasterHealthContract(contract, guard) {
|
||||
"reverse link direction"
|
||||
);
|
||||
assert.equal(contract.reverseLink?.client, "hwlab-tunnel-client", "reverse link client");
|
||||
assert.equal(contract.reverseLink?.publicPort, 6667, "reverse link public port");
|
||||
assert.equal(contract.reverseLink?.publicPort, 16667, "reverse link public port");
|
||||
assertForbiddenList(contract.forbiddenRuntimeSubstitutes, "master forbidden substitutes");
|
||||
|
||||
assertArray(contract.contracts, "master health contracts");
|
||||
|
||||
@@ -18,7 +18,7 @@ export const gateSummary = {
|
||||
"devOnly": true,
|
||||
"prodDisabled": true,
|
||||
"environment": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"namespace": "hwlab-dev",
|
||||
"sourceSummary": "The DEV gate is anchored to the frozen matrix, gate contract, and M0-M5 milestone docs on origin/main.",
|
||||
"localSmoke": {
|
||||
@@ -44,7 +44,7 @@ export const gateSummary = {
|
||||
"validated 2 evidence chain fixtures and 2 evidence records",
|
||||
"[m1-smoke] passed",
|
||||
"[m2-smoke] validated 11 service contracts",
|
||||
"[m2-smoke] mode=dry-run endpoint=http://74.48.78.17:6667",
|
||||
"[m2-smoke] mode=dry-run endpoint=http://74.48.78.17:16667",
|
||||
"[m2-smoke] no real DEV/PROD request was made",
|
||||
"M3 hardware loop smoke passed",
|
||||
"[m4-smoke] ok m4-agent-automation-loop-local-contract-smoke"
|
||||
@@ -59,7 +59,7 @@ export const gateSummary = {
|
||||
"node scripts/m5-mvp-e2e-dry-run.mjs"
|
||||
],
|
||||
"evidence": [
|
||||
"hwlab-cli health -> status: ok, environment: dev, endpoint: http://74.48.78.17:6667, projects: 2",
|
||||
"hwlab-cli health -> status: ok, environment: dev, endpoint: http://74.48.78.17:16667, projects: 2",
|
||||
"hwlab-cli dry-run -> 9 steps, 13 artifacts, 14 health contracts, 2 hardware operations, 2 evidence records, 0 network calls",
|
||||
"M5 MVP E2E dry-run passed: 9 steps, 13 artifacts, 14 health contracts, 2 hardware operations, 2 evidence records, 0 network calls"
|
||||
],
|
||||
@@ -68,17 +68,17 @@ export const gateSummary = {
|
||||
"devPreconditions": {
|
||||
"status": "blocked",
|
||||
"requirements": [
|
||||
"Obtain a live HTTP 200/JSON health observation from http://74.48.78.17:6667/health and /live",
|
||||
"Obtain a live HTTP 200/JSON health observation from http://74.48.78.17:16667/health and /live",
|
||||
"Record live route observations for hwlab-edge-proxy, hwlab-tunnel-client, hwlab-router, hwlab-cloud-api/web, hwlab-gateway, hwlab-gateway-simu, hwlab-box-simu, hwlab-patch-panel, hwlab-agent-mgr, hwlab-agent-worker, and hwlab-agent-skills",
|
||||
"Keep the run DEV-only and avoid PROD, heavy e2e, secrets, restarts, or UniDesk substitution"
|
||||
],
|
||||
"commands": [
|
||||
"curl -fsS --max-time 5 http://74.48.78.17:6667/health",
|
||||
"curl -fsS --max-time 5 http://74.48.78.17:6667/live"
|
||||
"curl -fsS --max-time 5 http://74.48.78.17:16667/health",
|
||||
"curl -fsS --max-time 5 http://74.48.78.17:16667/live"
|
||||
],
|
||||
"evidence": [
|
||||
"curl exit 7 for /health: could not connect to 74.48.78.17 port 6667",
|
||||
"curl exit 7 for /live: could not connect to 74.48.78.17 port 6667"
|
||||
"curl exit 7 for /health: could not connect to 74.48.78.17 port 16667",
|
||||
"curl exit 7 for /live: could not connect to 74.48.78.17 port 16667"
|
||||
],
|
||||
"summary": "The frozen DEV endpoint is not reachable from this runner, so no live DEV gate can be claimed yet."
|
||||
},
|
||||
@@ -87,7 +87,7 @@ export const gateSummary = {
|
||||
"type": "network_blocker",
|
||||
"scope": "devPreconditions",
|
||||
"status": "open",
|
||||
"summary": "http://74.48.78.17:6667/health and /live both fail with curl exit 7; live DEV ingress evidence is missing."
|
||||
"summary": "http://74.48.78.17:16667/health and /live both fail with curl exit 7; live DEV ingress evidence is missing."
|
||||
}
|
||||
],
|
||||
"milestones": [
|
||||
@@ -296,7 +296,7 @@ export const gateSummary = {
|
||||
"component": "DEV ingress",
|
||||
"status": "healthy",
|
||||
"deployEnv": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667/health",
|
||||
"endpoint": "http://74.48.78.17:16667/health",
|
||||
"observedBy": "fixture",
|
||||
"dryRun": true,
|
||||
"healthTimestamp": "2026-05-21T00:00:00.000Z"
|
||||
@@ -307,7 +307,7 @@ export const gateSummary = {
|
||||
"component": "master edge proxy",
|
||||
"status": "healthy",
|
||||
"deployEnv": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"observedBy": "fixture",
|
||||
"dryRun": true,
|
||||
"healthTimestamp": "2026-05-21T00:00:00.000Z"
|
||||
@@ -351,7 +351,7 @@ export const gateSummary = {
|
||||
"component": "Cloud Web",
|
||||
"status": "healthy",
|
||||
"deployEnv": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"observedBy": "fixture",
|
||||
"dryRun": true,
|
||||
"healthTimestamp": "2026-05-21T00:00:00.000Z"
|
||||
@@ -439,7 +439,7 @@ export const gateSummary = {
|
||||
"component": "CLI",
|
||||
"status": "healthy",
|
||||
"deployEnv": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"observedBy": "fixture",
|
||||
"dryRun": true,
|
||||
"healthTimestamp": "2026-05-21T00:00:00.000Z"
|
||||
@@ -455,7 +455,7 @@ export const gateSummary = {
|
||||
"gatewayId": "gtw_m5-dev-boundary",
|
||||
"status": "connected",
|
||||
"environment": "dev",
|
||||
"endpoint": "http://74.48.78.17:6667/gateway"
|
||||
"endpoint": "http://74.48.78.17:16667/gateway"
|
||||
},
|
||||
{
|
||||
"gatewaySessionId": "gws_m5-simu-0001",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const runtime = {
|
||||
endpoints: {
|
||||
dev: "http://74.48.78.17:6667"
|
||||
dev: "http://74.48.78.17:16667"
|
||||
},
|
||||
serviceRoute: [
|
||||
"browser/CLI/gateway",
|
||||
|
||||
Reference in New Issue
Block a user