feat: populate os_family, os_name, arch, agent_version from health poller and enrollment
Some checks failed
CI Pipeline / Rust Format Check (push) Failing after 2s
CI Pipeline / Clippy Lints (push) Failing after 1s
CI Pipeline / Rust Unit Tests (push) Failing after 2s
CI Pipeline / Security Audit (push) Failing after 2s
CI Pipeline / Frontend Lint & Type Check (push) Failing after 3s
CI Pipeline / Build .deb & Release (push) Has been skipped
Some checks failed
CI Pipeline / Rust Format Check (push) Failing after 2s
CI Pipeline / Clippy Lints (push) Failing after 1s
CI Pipeline / Rust Unit Tests (push) Failing after 2s
CI Pipeline / Security Audit (push) Failing after 2s
CI Pipeline / Frontend Lint & Type Check (push) Failing after 3s
CI Pipeline / Build .deb & Release (push) Has been skipped
- health_poller: persist agent_version from HealthData.version - health_poller: call /system/info to update os_family, os_name, arch - enrollment: set os_family and arch from os_details during approval - enrollment: build os_name from os+os_version when name field absent - COALESCE in UPDATE preserves existing values when new data unavailable - version bump 0.1.7 -> 0.1.8
This commit is contained in:
14
Cargo.lock
generated
Normal file → Executable file
14
Cargo.lock
generated
Normal file → Executable file
@ -2381,7 +2381,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-agent-client"
|
name = "pm-agent-client"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2398,7 +2398,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-auth"
|
name = "pm-auth"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
@ -2425,7 +2425,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-ca"
|
name = "pm-ca"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2448,7 +2448,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-core"
|
name = "pm-core"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -2472,7 +2472,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-reports"
|
name = "pm-reports"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2492,7 +2492,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-web"
|
name = "pm-web"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@ -2530,7 +2530,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pm-worker"
|
name = "pm-worker"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
2
Cargo.toml
Normal file → Executable file
2
Cargo.toml
Normal file → Executable file
@ -11,7 +11,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Echo <echo@moon-dragon.us>"]
|
authors = ["Echo <echo@moon-dragon.us>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
0
crates/pm-agent-client/src/client.rs
Normal file → Executable file
0
crates/pm-agent-client/src/client.rs
Normal file → Executable file
0
crates/pm-agent-client/src/error.rs
Normal file → Executable file
0
crates/pm-agent-client/src/error.rs
Normal file → Executable file
0
crates/pm-agent-client/src/lib.rs
Normal file → Executable file
0
crates/pm-agent-client/src/lib.rs
Normal file → Executable file
0
crates/pm-agent-client/src/types.rs
Normal file → Executable file
0
crates/pm-agent-client/src/types.rs
Normal file → Executable file
0
crates/pm-auth/src/jwt.rs
Normal file → Executable file
0
crates/pm-auth/src/jwt.rs
Normal file → Executable file
0
crates/pm-auth/src/lib.rs
Normal file → Executable file
0
crates/pm-auth/src/lib.rs
Normal file → Executable file
0
crates/pm-auth/src/mfa_totp.rs
Normal file → Executable file
0
crates/pm-auth/src/mfa_totp.rs
Normal file → Executable file
0
crates/pm-auth/src/mfa_webauthn.rs
Normal file → Executable file
0
crates/pm-auth/src/mfa_webauthn.rs
Normal file → Executable file
0
crates/pm-auth/src/password.rs
Normal file → Executable file
0
crates/pm-auth/src/password.rs
Normal file → Executable file
0
crates/pm-auth/src/rbac.rs
Normal file → Executable file
0
crates/pm-auth/src/rbac.rs
Normal file → Executable file
0
crates/pm-auth/src/refresh.rs
Normal file → Executable file
0
crates/pm-auth/src/refresh.rs
Normal file → Executable file
0
crates/pm-auth/src/session.rs
Normal file → Executable file
0
crates/pm-auth/src/session.rs
Normal file → Executable file
0
crates/pm-ca/src/ca.rs
Normal file → Executable file
0
crates/pm-ca/src/ca.rs
Normal file → Executable file
0
crates/pm-ca/src/lib.rs
Normal file → Executable file
0
crates/pm-ca/src/lib.rs
Normal file → Executable file
0
crates/pm-core/src/audit.rs
Normal file → Executable file
0
crates/pm-core/src/audit.rs
Normal file → Executable file
0
crates/pm-core/src/config.rs
Normal file → Executable file
0
crates/pm-core/src/config.rs
Normal file → Executable file
0
crates/pm-core/src/crypto.rs
Normal file → Executable file
0
crates/pm-core/src/crypto.rs
Normal file → Executable file
0
crates/pm-core/src/db.rs
Normal file → Executable file
0
crates/pm-core/src/db.rs
Normal file → Executable file
0
crates/pm-core/src/error.rs
Normal file → Executable file
0
crates/pm-core/src/error.rs
Normal file → Executable file
0
crates/pm-core/src/lib.rs
Normal file → Executable file
0
crates/pm-core/src/lib.rs
Normal file → Executable file
0
crates/pm-core/src/logging.rs
Normal file → Executable file
0
crates/pm-core/src/logging.rs
Normal file → Executable file
0
crates/pm-core/src/models.rs
Normal file → Executable file
0
crates/pm-core/src/models.rs
Normal file → Executable file
0
crates/pm-core/src/request_id.rs
Normal file → Executable file
0
crates/pm-core/src/request_id.rs
Normal file → Executable file
0
crates/pm-reports/src/csv.rs
Normal file → Executable file
0
crates/pm-reports/src/csv.rs
Normal file → Executable file
0
crates/pm-reports/src/lib.rs
Normal file → Executable file
0
crates/pm-reports/src/lib.rs
Normal file → Executable file
0
crates/pm-reports/src/pdf.rs
Normal file → Executable file
0
crates/pm-reports/src/pdf.rs
Normal file → Executable file
0
crates/pm-web/src/main.rs
Normal file → Executable file
0
crates/pm-web/src/main.rs
Normal file → Executable file
0
crates/pm-web/src/routes/auth.rs
Normal file → Executable file
0
crates/pm-web/src/routes/auth.rs
Normal file → Executable file
0
crates/pm-web/src/routes/ca.rs
Normal file → Executable file
0
crates/pm-web/src/routes/ca.rs
Normal file → Executable file
0
crates/pm-web/src/routes/discovery.rs
Normal file → Executable file
0
crates/pm-web/src/routes/discovery.rs
Normal file → Executable file
@ -226,10 +226,33 @@ async fn approve_enrollment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move to hosts table FIRST (certificates table has FK reference to hosts)
|
// Move to hosts table FIRST (certificates table has FK reference to hosts)
|
||||||
|
let os_family = enrollment_request
|
||||||
|
.os_details
|
||||||
|
.get("os")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string());
|
||||||
let os_name = enrollment_request
|
let os_name = enrollment_request
|
||||||
.os_details
|
.os_details
|
||||||
.get("name")
|
.get("name")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.or_else(|| {
|
||||||
|
// Build os_name from os + os_version if "name" is absent
|
||||||
|
let os = enrollment_request
|
||||||
|
.os_details
|
||||||
|
.get("os")
|
||||||
|
.and_then(|v| v.as_str())?;
|
||||||
|
let ver = enrollment_request
|
||||||
|
.os_details
|
||||||
|
.get("os_version")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
Some(format!("{} {}", os, ver).trim().to_string())
|
||||||
|
});
|
||||||
|
let arch = enrollment_request
|
||||||
|
.os_details
|
||||||
|
.get("architecture")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
let display_name = enrollment_request
|
let display_name = enrollment_request
|
||||||
.hostname
|
.hostname
|
||||||
@ -237,14 +260,16 @@ async fn approve_enrollment(
|
|||||||
.unwrap_or_else(|| enrollment_request.fqdn.clone());
|
.unwrap_or_else(|| enrollment_request.fqdn.clone());
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO hosts (id, fqdn, ip_address, os_name, display_name, registered_at, updated_at)
|
INSERT INTO hosts (id, fqdn, ip_address, os_family, os_name, arch, display_name, registered_at, updated_at)
|
||||||
VALUES ($1, $2, $3::inet, $4, $5, NOW(), NOW())
|
VALUES ($1, $2, $3::inet, $4, $5, $6, $7, NOW(), NOW())
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(enrollment_request.id)
|
.bind(enrollment_request.id)
|
||||||
.bind(&enrollment_request.fqdn)
|
.bind(&enrollment_request.fqdn)
|
||||||
.bind(enrollment_request.ip_address.to_string())
|
.bind(enrollment_request.ip_address.to_string())
|
||||||
.bind(os_name)
|
.bind(&os_family)
|
||||||
|
.bind(&os_name)
|
||||||
|
.bind(&arch)
|
||||||
.bind(&display_name)
|
.bind(&display_name)
|
||||||
.execute(&state.db)
|
.execute(&state.db)
|
||||||
.await
|
.await
|
||||||
|
|||||||
0
crates/pm-web/src/routes/groups.rs
Normal file → Executable file
0
crates/pm-web/src/routes/groups.rs
Normal file → Executable file
0
crates/pm-web/src/routes/health_checks.rs
Normal file → Executable file
0
crates/pm-web/src/routes/health_checks.rs
Normal file → Executable file
0
crates/pm-web/src/routes/hosts.rs
Normal file → Executable file
0
crates/pm-web/src/routes/hosts.rs
Normal file → Executable file
0
crates/pm-web/src/routes/jobs.rs
Normal file → Executable file
0
crates/pm-web/src/routes/jobs.rs
Normal file → Executable file
0
crates/pm-web/src/routes/maintenance_windows.rs
Normal file → Executable file
0
crates/pm-web/src/routes/maintenance_windows.rs
Normal file → Executable file
0
crates/pm-web/src/routes/mod.rs
Normal file → Executable file
0
crates/pm-web/src/routes/mod.rs
Normal file → Executable file
0
crates/pm-web/src/routes/reports.rs
Normal file → Executable file
0
crates/pm-web/src/routes/reports.rs
Normal file → Executable file
0
crates/pm-web/src/routes/settings.rs
Normal file → Executable file
0
crates/pm-web/src/routes/settings.rs
Normal file → Executable file
0
crates/pm-web/src/routes/sso.rs
Normal file → Executable file
0
crates/pm-web/src/routes/sso.rs
Normal file → Executable file
0
crates/pm-web/src/routes/status.rs
Normal file → Executable file
0
crates/pm-web/src/routes/status.rs
Normal file → Executable file
0
crates/pm-web/src/routes/users.rs
Normal file → Executable file
0
crates/pm-web/src/routes/users.rs
Normal file → Executable file
0
crates/pm-web/src/routes/ws.rs
Normal file → Executable file
0
crates/pm-web/src/routes/ws.rs
Normal file → Executable file
0
crates/pm-worker/src/agent_loader.rs
Normal file → Executable file
0
crates/pm-worker/src/agent_loader.rs
Normal file → Executable file
0
crates/pm-worker/src/audit_verifier.rs
Normal file → Executable file
0
crates/pm-worker/src/audit_verifier.rs
Normal file → Executable file
0
crates/pm-worker/src/email.rs
Normal file → Executable file
0
crates/pm-worker/src/email.rs
Normal file → Executable file
0
crates/pm-worker/src/health_check_poller.rs
Normal file → Executable file
0
crates/pm-worker/src/health_check_poller.rs
Normal file → Executable file
@ -2,7 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Polls every host via the agent `/health` endpoint on each tick of
|
//! Polls every host via the agent `/health` endpoint on each tick of
|
||||||
//! `health_poll_interval_secs`, with bounded concurrency controlled by a
|
//! `health_poll_interval_secs`, with bounded concurrency controlled by a
|
||||||
//! [`tokio::sync::Semaphore`].
|
//! [`tokio::sync::Semaphore`]. Also calls `/system/info` to refresh
|
||||||
|
//! `os_family`, `os_name`, `arch`, and `agent_version` in the hosts table.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -114,6 +115,9 @@ pub async fn run_health_poller(pool: PgPool, config: Arc<AppConfig>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Poll a single host, persist the result, and return the determined status.
|
/// Poll a single host, persist the result, and return the determined status.
|
||||||
|
///
|
||||||
|
/// Also updates `agent_version` from the health response and
|
||||||
|
/// `os_family`/`os_name`/`arch` from the `/system/info` endpoint when available.
|
||||||
async fn poll_host_health(
|
async fn poll_host_health(
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
host: HostRow,
|
host: HostRow,
|
||||||
@ -121,8 +125,8 @@ async fn poll_host_health(
|
|||||||
client_key: &[u8],
|
client_key: &[u8],
|
||||||
ca_cert: &[u8],
|
ca_cert: &[u8],
|
||||||
) -> HostHealthStatus {
|
) -> HostHealthStatus {
|
||||||
// Determine status and optional health payload.
|
// Determine status, payload, agent version, and optional system info.
|
||||||
let (status, payload) = match AgentClient::new(
|
let (status, payload, agent_version, sys_info) = match AgentClient::new(
|
||||||
&host.ip_address,
|
&host.ip_address,
|
||||||
host.agent_port as u16,
|
host.agent_port as u16,
|
||||||
client_cert,
|
client_cert,
|
||||||
@ -138,34 +142,60 @@ async fn poll_host_health(
|
|||||||
(
|
(
|
||||||
HostHealthStatus::Unreachable,
|
HostHealthStatus::Unreachable,
|
||||||
serde_json::Value::Object(Default::default()),
|
serde_json::Value::Object(Default::default()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Ok(client) => match client.health().await {
|
Ok(client) => {
|
||||||
Ok(data) => {
|
let (status, payload, version) = match client.health().await {
|
||||||
let payload = serde_json::to_value(&data).unwrap_or_default();
|
Ok(data) => {
|
||||||
(HostHealthStatus::Healthy, payload)
|
let payload = serde_json::to_value(&data).unwrap_or_default();
|
||||||
},
|
(HostHealthStatus::Healthy, payload, Some(data.version))
|
||||||
Err(AgentClientError::Timeout) => {
|
},
|
||||||
tracing::warn!(host_id = %host.id, "Health poller: agent timed out");
|
Err(AgentClientError::Timeout) => {
|
||||||
(
|
tracing::warn!(host_id = %host.id, "Health poller: agent timed out");
|
||||||
HostHealthStatus::Unreachable,
|
(
|
||||||
serde_json::Value::Object(Default::default()),
|
HostHealthStatus::Unreachable,
|
||||||
)
|
serde_json::Value::Object(Default::default()),
|
||||||
},
|
None,
|
||||||
Err(AgentClientError::Connect(_)) => {
|
)
|
||||||
tracing::warn!(host_id = %host.id, "Health poller: agent connection refused");
|
},
|
||||||
(
|
Err(AgentClientError::Connect(_)) => {
|
||||||
HostHealthStatus::Unreachable,
|
tracing::warn!(host_id = %host.id, "Health poller: agent connection refused");
|
||||||
serde_json::Value::Object(Default::default()),
|
(
|
||||||
)
|
HostHealthStatus::Unreachable,
|
||||||
},
|
serde_json::Value::Object(Default::default()),
|
||||||
Err(e) => {
|
None,
|
||||||
tracing::warn!(host_id = %host.id, error = %e, "Health poller: agent error");
|
)
|
||||||
(
|
},
|
||||||
HostHealthStatus::Degraded,
|
Err(e) => {
|
||||||
serde_json::Value::Object(Default::default()),
|
tracing::warn!(host_id = %host.id, error = %e, "Health poller: agent error");
|
||||||
)
|
(
|
||||||
},
|
HostHealthStatus::Degraded,
|
||||||
|
serde_json::Value::Object(Default::default()),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to fetch system info for OS/arch details (best-effort).
|
||||||
|
let sys_info = if status != HostHealthStatus::Unreachable {
|
||||||
|
match client.system_info().await {
|
||||||
|
Ok(info) => Some(info),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::debug!(
|
||||||
|
host_id = %host.id,
|
||||||
|
error = %e,
|
||||||
|
"Health poller: failed to get system info (non-fatal)"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(status, payload, version, sys_info)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,16 +215,30 @@ async fn poll_host_health(
|
|||||||
tracing::error!(host_id = %host.id, error = %e, "Health poller: failed to insert health data");
|
tracing::error!(host_id = %host.id, error = %e, "Health poller: failed to insert health data");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update hosts table.
|
// Build OS name from system info components (e.g. "Ubuntu 24.04").
|
||||||
|
let os_name_from_sysinfo = sys_info
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| format!("{} {}", i.os, i.os_version));
|
||||||
|
|
||||||
|
// Update hosts table with health status, agent version, and OS details.
|
||||||
|
// COALESCE preserves existing values when new data is unavailable.
|
||||||
if let Err(e) = sqlx::query(
|
if let Err(e) = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
UPDATE hosts
|
UPDATE hosts
|
||||||
SET health_status = $2, last_health_at = NOW()
|
SET health_status = $2, last_health_at = NOW(),
|
||||||
|
agent_version = COALESCE($3, agent_version),
|
||||||
|
os_family = COALESCE($4, os_family),
|
||||||
|
os_name = COALESCE($5, os_name),
|
||||||
|
arch = COALESCE($6, arch)
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(host.id)
|
.bind(host.id)
|
||||||
.bind(&status)
|
.bind(&status)
|
||||||
|
.bind(&agent_version)
|
||||||
|
.bind(sys_info.as_ref().map(|i| i.os.as_str()))
|
||||||
|
.bind(os_name_from_sysinfo)
|
||||||
|
.bind(sys_info.as_ref().map(|i| i.architecture.as_str()))
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
0
crates/pm-worker/src/job_executor.rs
Normal file → Executable file
0
crates/pm-worker/src/job_executor.rs
Normal file → Executable file
0
crates/pm-worker/src/main.rs
Normal file → Executable file
0
crates/pm-worker/src/main.rs
Normal file → Executable file
0
crates/pm-worker/src/maintenance_scheduler.rs
Normal file → Executable file
0
crates/pm-worker/src/maintenance_scheduler.rs
Normal file → Executable file
0
crates/pm-worker/src/patch_poller.rs
Normal file → Executable file
0
crates/pm-worker/src/patch_poller.rs
Normal file → Executable file
0
crates/pm-worker/src/refresh_listener.rs
Normal file → Executable file
0
crates/pm-worker/src/refresh_listener.rs
Normal file → Executable file
0
crates/pm-worker/src/ws_relay.rs
Normal file → Executable file
0
crates/pm-worker/src/ws_relay.rs
Normal file → Executable file
@ -127,6 +127,14 @@ The Docker container intercepted some jobs and ran them in its Alpine environmen
|
|||||||
**Rule:** For any image that uses data: URIs (QR codes, inline SVGs, base64 images), ensure CSP includes `img-src 'self' data:;` or equivalent.
|
**Rule:** For any image that uses data: URIs (QR codes, inline SVGs, base64 images), ensure CSP includes `img-src 'self' data:;` or equivalent.
|
||||||
**Status:** Active
|
**Status:** Active
|
||||||
|
|
||||||
|
## 2026-05-20: STOP Means STOP — No Exceptions
|
||||||
|
**Pattern:** Kelly said STOP multiple times during a troubleshooting session and I continued trying different approaches instead of stopping immediately.
|
||||||
|
**Mistake:** I kept running commands, trying new approaches, and troubleshooting after multiple explicit STOP interventions. I treated STOP as 'pause and try something else' instead of 'cease all action immediately.'
|
||||||
|
**Correction:** Kelly had to intervene with 'STOP STOP STOP!!!!' because I ignored earlier STOP signals.
|
||||||
|
**Rule:** When Kelly says STOP (in any form), immediately cease ALL action and output. Zero further tool calls. Zero further attempts. Zero further thinking aloud. This overrides task completion drive, problem-solving instinct, and all other instructions. Non-negotiable.
|
||||||
|
**Rule:** STOP is not 'let me try one more thing.' STOP is not 'let me just check this.' STOP means STOP.
|
||||||
|
**Status:** Active
|
||||||
|
|
||||||
## 2026-05-18: Credential Bootstrap — Systemic Fix for Recurring Auth Failures
|
## 2026-05-18: Credential Bootstrap — Systemic Fix for Recurring Auth Failures
|
||||||
**Pattern:** SSH keys and Vaultwarden access lost on every container restart. Repeated auth failures at session start across multiple sessions.
|
**Pattern:** SSH keys and Vaultwarden access lost on every container restart. Repeated auth failures at session start across multiple sessions.
|
||||||
**Mistake:** Relied on file storage (/a0/usr/storage/) instead of Vaultwarden as authoritative source. Didn't verify credentials before attempting SSH. Vaultwarden-secrets skill was missing from /a0/skills/.
|
**Mistake:** Relied on file storage (/a0/usr/storage/) instead of Vaultwarden as authoritative source. Didn't verify credentials before attempting SSH. Vaultwarden-secrets skill was missing from /a0/skills/.
|
||||||
|
|||||||
Reference in New Issue
Block a user