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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jsonwebtoken = "9"
pulldown-cmark = "0.13.0"
hickory-resolver = "0.24.4"
socket2 = "0.6"
zeroize = "1.8.2"
zeroize = { version = "1.8.2", features = ["serde"] }

[build-dependencies]
# Doc comment scanner (build.rs extracts /// @ntnt blocks)
Expand Down
16 changes: 16 additions & 0 deletions docs/RUNTIME_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Environment variables that control NTNT runtime behavior
| `NTNT_LINT_MODE` | `default`, `warn`, `strict` | default | Controls lint strictness for type annotations. `default`: only check annotated code. `warn`: also warn about missing annotations (non-fatal). `strict`: missing annotations are errors. CLI flags (`--strict`, `--warn-untyped`) override this. |
| `NTNT_MAX_RECURSION` | integer | 256 | Maximum recursion depth for function calls. Prevents stack overflow from runaway recursion. |
| `NTNT_NET_ALLOW_PRIVATE` | `1`, `true`, `yes` | unset (disabled — private targets blocked) | Process-level opt-in for `std/net` probes (`ping`, `tcp_connect`, `reachable`, `port_scan`, `tls_info`, `traceroute`) against private/internal targets. Each call must also pass `allow_private: true`. Special-purpose targets such as cloud metadata, multicast, broadcast, unspecified, and documentation ranges remain blocked. This is separate from `NTNT_ALLOW_PRIVATE_IPS`, which only applies to `fetch()`. |
| `NTNT_SECRETS_AUTHORIZATION_SCOPE` | opaque ASCII deployment identifier | unset (required for larri-socket) | Trusted non-credential deployment scope expected in every Larri agent response. It detects endpoint miswiring but is not authentication and is never rendered in diagnostics. Leading or trailing whitespace is rejected. |
| `NTNT_SECRETS_PROVIDER` | `env`, `larri-socket` | env | Selects the provider behind `std/secrets`. The exact-name environment provider is development-only and is rejected when `NTNT_ENV` is `production` or `prod`. `larri-socket` is Unix-only and talks to a local Larri host agent without depending directly on a secrets backend. |
| `NTNT_SECRETS_SOCKET_ENDPOINTS` | comma-separated absolute Unix socket paths | unset (required for larri-socket) | Ordered Larri host-agent endpoints for secret lookup. One through eight unique paths are allowed. Each endpoint is tried twice, and failover occurs only for bounded unavailable results. Production paths must be beneath the host-controlled `/run/larri-secrets` root; raw paths are never included in runtime diagnostics. |
| `NTNT_SECRETS_TIMEOUT_MS` | integer (milliseconds) | 1000 | Per-attempt Larri socket connect, write, and read timeout. Values must be between 10 and 10000 milliseconds. |
| `NTNT_STRICT` | `1`, `true` | unset (disabled) | **Deprecated — use `NTNT_LINT_MODE=strict` instead.** Enable strict type checking. Still works but emits a deprecation warning. |
| `NTNT_TIMEOUT` | integer (seconds) | 30 | Request timeout for HTTP server in seconds. |
| `NTNT_TYPE_MODE` | `strict`, `warn`, `forgiving` | warn | Controls runtime behavior for type mismatches. `strict`: type mismatches crash (fail-closed, recommended for auth/payments). `warn`: log `[WARN]` and continue (default). `forgiving`: silent degradation (pre-v0.4 behavior). See [Type Safety Modes](#type-safety-modes). |
Expand All @@ -54,6 +58,18 @@ NTNT_MAX_RECURSION=512 ntnt run server.tnt
# Process-level opt-in for `std/net` probes (`ping`, `tcp_connect`, `reachable`, `port_scan`, `tls_info`, `traceroute`) against private/internal targets
NTNT_NET_ALLOW_PRIVATE=1 ntnt run monitor.tnt

# Trusted non-credential deployment scope expected in every Larri agent response
NTNT_SECRETS_AUTHORIZATION_SCOPE=deployment-a

# Selects the provider behind `std/secrets`
NTNT_SECRETS_PROVIDER=larri-socket ntnt run server.tnt

# Ordered Larri host-agent endpoints for secret lookup
NTNT_SECRETS_SOCKET_ENDPOINTS=/run/larri-secrets/secrets.sock

# Per-attempt Larri socket connect, write, and read timeout
NTNT_SECRETS_TIMEOUT_MS=500

# **Deprecated — use `NTNT_LINT_MODE=strict` instead.** Enable strict type checking
NTNT_STRICT=1 ntnt run server.tnt

Expand Down
4 changes: 2 additions & 2 deletions docs/STDLIB_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10905,7 +10905,7 @@ get_secret(name: String) -> Option<Secret>

Looks up a secret by its provider-neutral logical name.

The v0.5.1 development-only environment provider reads the exact environment variable name and is disabled when `NTNT_ENV` is `production` or `prod`. Projects with an `ntnt.toml` must declare accessible names under `[secrets.<NAME>]`; undeclared lookups fail before contacting the provider. Declaration metadata may contain `label`, `description`, `required`, and `environments`; secret values are never accepted in the manifest. Secret values remain opaque and redact themselves in output and diagnostics.
The environment provider reads the exact environment variable name and is disabled when `NTNT_ENV` is `production` or `prod`. Unix deployments can instead select the Larri host-agent provider with `NTNT_SECRETS_PROVIDER=larri-socket`, a comma-separated list of absolute `NTNT_SECRETS_SOCKET_ENDPOINTS`, and the expected non-credential deployment identifier in `NTNT_SECRETS_AUTHORIZATION_SCOPE`. Optional `NTNT_SECRETS_TIMEOUT_MS` is bounded from 10 through 10000 milliseconds. Production socket paths must be beneath `/run/larri-secrets`; endpoints are retried twice and failed over only for bounded unavailable results. Plaintext caching remains in the host agent rather than ntnt. Projects with an `ntnt.toml` must declare accessible names under `[secrets.<NAME>]`; undeclared lookups fail before contacting the provider. Declaration metadata may contain `label`, `description`, `required`, and `environments`; secret values are never accepted in the manifest. Secret values remain opaque and redact themselves in output and diagnostics.

**Parameters:**

Expand All @@ -10921,7 +10921,7 @@ get_secret("STRIPE_SECRET_KEY") // => Some([REDACTED]) // Optional lookup

**Errors:**

- **RuntimeError**: Unsupported secrets provider — *Fix: Set NTNT_SECRETS_PROVIDER=env for v0.5.1*
- **RuntimeError**: Unsupported secrets provider — *Fix: Select env for development or larri-socket with deployment-scoped socket configuration*

**See also:** `require_secret`

Expand Down
28 changes: 28 additions & 0 deletions docs/runtime.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ description = "Controls runtime mode. Production mode disables hot-reload for be
example = "NTNT_ENV=production ntnt run server.tnt"
affects = ["hot-reload"]

[env_vars.NTNT_SECRETS_PROVIDER]
values = ["env", "larri-socket"]
default = "env"
description = "Selects the provider behind `std/secrets`. The exact-name environment provider is development-only and is rejected when `NTNT_ENV` is `production` or `prod`. `larri-socket` is Unix-only and talks to a local Larri host agent without depending directly on a secrets backend."
example = "NTNT_SECRETS_PROVIDER=larri-socket ntnt run server.tnt"
affects = ["std-secrets", "security"]

[env_vars.NTNT_SECRETS_SOCKET_ENDPOINTS]
type = "comma-separated absolute Unix socket paths"
default = "unset (required for larri-socket)"
description = "Ordered Larri host-agent endpoints for secret lookup. One through eight unique paths are allowed. Each endpoint is tried twice, and failover occurs only for bounded unavailable results. Production paths must be beneath the host-controlled `/run/larri-secrets` root; raw paths are never included in runtime diagnostics."
example = "NTNT_SECRETS_SOCKET_ENDPOINTS=/run/larri-secrets/secrets.sock"
affects = ["std-secrets", "security"]

[env_vars.NTNT_SECRETS_AUTHORIZATION_SCOPE]
type = "opaque ASCII deployment identifier"
default = "unset (required for larri-socket)"
description = "Trusted non-credential deployment scope expected in every Larri agent response. It detects endpoint miswiring but is not authentication and is never rendered in diagnostics. Leading or trailing whitespace is rejected."
example = "NTNT_SECRETS_AUTHORIZATION_SCOPE=deployment-a"
affects = ["std-secrets", "security"]

[env_vars.NTNT_SECRETS_TIMEOUT_MS]
type = "integer (milliseconds)"
default = "1000"
description = "Per-attempt Larri socket connect, write, and read timeout. Values must be between 10 and 10000 milliseconds."
example = "NTNT_SECRETS_TIMEOUT_MS=500"
affects = ["std-secrets", "security"]

[env_vars.NTNT_TIMEOUT]
type = "integer (seconds)"
default = "30"
Expand Down
9 changes: 8 additions & 1 deletion src/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ pub struct SecretValue {

impl SecretValue {
/// Construct a secret after validating its logical name.
#[cfg(test)]
pub(crate) fn new(name: impl Into<String>, value: impl Into<String>) -> Result<Self> {
Self::new_zeroizing(name, Zeroizing::new(value.into()))
}

/// Construct a secret from provider-owned zeroizing storage without copying
/// plaintext through an ordinary String at the provider boundary.
pub(crate) fn new_zeroizing(name: impl Into<String>, value: Zeroizing<String>) -> Result<Self> {
let name = name.into();
validate_secret_name(&name)?;
Ok(Self {
name: Arc::from(name),
value: Arc::new(Zeroizing::new(value.into())),
value: Arc::new(value),
})
}

Expand Down
Loading
Loading