Skip to content

Implement basic runes and services installation - #3

Merged
wlix13 merged 7 commits into
mainfrom
feature/implement-runes
Jul 25, 2026
Merged

Implement basic runes and services installation#3
wlix13 merged 7 commits into
mainfrom
feature/implement-runes

Conversation

@wlix13

@wlix13 wlix13 commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Type of change

  • Bug fix (fixes an issue in a rune, mold or deploy behaviour)
  • Feature (adds a new rune, mold or CLI capability)
  • Enhancement (improves existing provisioning logic or output)
  • Refactor (restructures code without changing behaviour)
  • Breaking change (changes existing mold schemas, CLI usage, or deploy behaviour)
  • Security (security-related fix or hardening)

Description

Why is this change needed?

Related Issues

Testing

  • Unit tests added or updated (uv run poe tests)
  • Deployed to a real host
  • No testing required (documentation changes only)

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Linter and type checker pass (uv run poe check)
  • Documentation updated (if applicable)

Summary by CodeRabbit

  • New Features
    • Expanded infrastructure-as-code framework with configurable molds, validation, and example inventory presets.
    • Added end-to-end provisioning for base system, user management, containers, DNS, monitoring (Nezha), profiles, HAProxy, SSH/network hardening, and proxy stacks (WARP, Telemt, Tor, Xray, Zero Trust Tunnel).
    • Introduced deployment runes covering installation, templating, and systemd integration.
  • Documentation
    • Added initial NullForge documentation with core concepts and install/deploy instructions.
  • Chores
    • Updated repository ignore rules (macOS metadata) and refined inventory example handling.

wlix13 added 7 commits April 12, 2026 15:00
- `prepare`: minimal needed packages for NullForge to work
- `base`: basic system configuration
- `containers`: container management
- `dns`: DoH/DoT/DoU resolver configuration
- `haproxy`: HAProxt installation
- `monitoring`: monitoring system configuration
- `netsec`: network security policies
- `profiles`: user profiles management
- `users`: user management and SSH keys management
- `telemt`: Telemt proxy installation
- `tor`: Tor proxy installation
- `warp`: Cloudflare Warp installation
- `xray`: Xray-Core installation
- `zerotrust`: Cloudflare ZeroTrust proxy installation
@wlix13 wlix13 self-assigned this Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@wlix13, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9df62eb1-652b-4c91-85fe-ab7c109ac1df

📥 Commits

Reviewing files that changed from the base of the PR and between 96d6115 and 951f6d5.

📒 Files selected for processing (99)
  • .gitignore
  • .release-please-manifest.json
  • README.md
  • nullforge/__init__.py
  • nullforge/foundry/README.md
  • nullforge/foundry/__init__.py
  • nullforge/foundry/full_cast.py
  • nullforge/inventories/README.md
  • nullforge/inventories/example.py
  • nullforge/models/__init__.py
  • nullforge/models/containers.py
  • nullforge/models/dns.py
  • nullforge/models/monitoring/__init__.py
  • nullforge/models/monitoring/base.py
  • nullforge/models/monitoring/nezha.py
  • nullforge/models/netsec.py
  • nullforge/models/profiles.py
  • nullforge/models/system.py
  • nullforge/models/users.py
  • nullforge/models/warp.py
  • nullforge/models/zerotrust.py
  • nullforge/molds/__init__.py
  • nullforge/molds/base_mold.py
  • nullforge/molds/containers.py
  • nullforge/molds/defaults.py
  • nullforge/molds/dns.py
  • nullforge/molds/features.py
  • nullforge/molds/haproxy.py
  • nullforge/molds/monitoring/__init__.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/molds/netsec.py
  • nullforge/molds/profiles.py
  • nullforge/molds/system.py
  • nullforge/molds/telemt.py
  • nullforge/molds/tor.py
  • nullforge/molds/user.py
  • nullforge/molds/utils.py
  • nullforge/molds/warp.py
  • nullforge/molds/xray.py
  • nullforge/molds/zerotrust.py
  • nullforge/runes/__init__.py
  • nullforge/runes/base.py
  • nullforge/runes/containers.py
  • nullforge/runes/dns.py
  • nullforge/runes/haproxy.py
  • nullforge/runes/monitoring.py
  • nullforge/runes/netsec.py
  • nullforge/runes/prepare.py
  • nullforge/runes/profiles.py
  • nullforge/runes/telemt.py
  • nullforge/runes/tor.py
  • nullforge/runes/users.py
  • nullforge/runes/warp.py
  • nullforge/runes/xray.py
  • nullforge/runes/zerotrust.py
  • nullforge/smithy/__init__.py
  • nullforge/smithy/admin.py
  • nullforge/smithy/arch.py
  • nullforge/smithy/blocky.py
  • nullforge/smithy/cloudflare.py
  • nullforge/smithy/github.py
  • nullforge/smithy/http.py
  • nullforge/smithy/install.py
  • nullforge/smithy/monitoring/__init__.py
  • nullforge/smithy/monitoring/nezha/__init__.py
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/smithy/monitoring/nezha/dashboard.py
  • nullforge/smithy/monitoring/nezha/deploy.py
  • nullforge/smithy/network.py
  • nullforge/smithy/packages.py
  • nullforge/smithy/service.py
  • nullforge/smithy/sni.py
  • nullforge/smithy/swap.py
  • nullforge/smithy/system.py
  • nullforge/smithy/versions.py
  • nullforge/templates/__init__.py
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/templates/dns/blocky.yaml.j2
  • nullforge/templates/dns/dns.yaml.j2
  • nullforge/templates/dns/resolv.conf.j2
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/templates/etc/default/zramswap.j2
  • nullforge/templates/nvim/nvim_patch.lua.j2
  • nullforge/templates/profiles/direnv.toml
  • nullforge/templates/profiles/starship.toml
  • nullforge/templates/profiles/tmux.conf
  • nullforge/templates/profiles/zshrc.j2
  • nullforge/templates/scripts/telemt-synfix.sh
  • nullforge/templates/scripts/teleproxy-warp.sh
  • nullforge/templates/scripts/warp-v6-policy.sh
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/systemd/blocky.service.j2
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/systemd/cloudflare-warp.service.j2
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/templates/systemd/telemt.service.j2
  • nullforge/templates/telemt/telemt.toml.j2
  • nullforge/templates/tor/torrc.j2
  • pyproject.toml
📝 Walkthrough

Walkthrough

NullForge adds a pyinfra-based infrastructure-as-code framework with validated configuration molds, deployment runes, reusable provisioning helpers, systemd and shell templates, example inventory data, and project documentation.

Changes

NullForge framework

Layer / File(s) Summary
Project contracts and deployment entrypoints
.gitignore, README.md, pyproject.toml, nullforge/foundry/*, nullforge/inventories/*, nullforge/molds/defaults.py, nullforge/molds/features.py, nullforge/molds/utils.py
Adds project documentation, inventory examples, feature merging and normalization, rune discovery, and full-cast dispatch.
Domain and backend models
nullforge/models/*
Adds typed configuration models for DNS, containers, monitoring, WARP, SSH security, swap, profiles, users, and Zero Trust protocols.
Validated configuration molds
nullforge/molds/*
Adds Pydantic molds with activation properties, field validation, cross-field validation, and sensitive-value redaction.
Provisioning utilities and integrations
nullforge/smithy/*
Adds package, network, HTTP, release installation, version, locale, swap, ACL, monitoring, GitHub, and TLS probing helpers.
Pyinfra deployment runes
nullforge/runes/*
Adds deployment flows for base systems, containers, DNS, network security, profiles, users, monitoring, proxies, WARP, Xray, Tor, HAProxy, and Zero Trust tunnels.
Templates and service artifacts
nullforge/templates/*
Adds Jinja configuration templates, systemd units, shell routing scripts, profile configuration, Telemt, Tor, DNS, swap, and cloud service artifacts.

Estimated code review effort: 5 (Critical) | ~180 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the core change: adding foundational runes and service deployment/install logic.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/implement-runes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs Documentation updates molds Configuration molds and domain models runes Provisioning runes smithy Cross-distro helpers and shared provisioning logic foundry Foundry entrypoints and inventories templates Templates updates labels Jul 24, 2026
@wlix13
wlix13 force-pushed the feature/implement-runes branch from 4ccf111 to d44ecad Compare July 24, 2026 16:04
@wlix13

wlix13 commented Jul 24, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (18)
nullforge/molds/profiles.py-11-14 (1)

11-14: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make for_root independent of managed-user creation.

nullforge/runes/profiles.py:48-61 currently requires user_opts.manage before adding ("root", "/root"). Thus for_root=True does nothing when the regular user is unmanaged, despite this field’s contract. Change that condition to if profiles_opts.for_root:.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/profiles.py` around lines 11 - 14, Update the profile
installation condition in the profiles handling logic around user_opts.manage so
the root profile entry is added whenever profiles_opts.for_root is true,
independently of managed-user creation; preserve the existing managed-user
handling separately.
nullforge/molds/haproxy.py-13-16 (1)

13-16: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor version on RHEL or reject unsupported version selection.

nullforge/runes/haproxy.py:25-103 uses packages=["haproxy"] on RHEL, so version="3.2" is ignored there. Either provision the requested version on RHEL or validate/document that version pinning is Debian-only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/haproxy.py` around lines 13 - 16, Update the HAProxy version
handling across the model field `version` and the provisioning logic in
`haproxy` so RHEL does not silently ignore a requested version: either install
the selected version on RHEL or validate the platform and reject/document
version pinning as Debian-only, while preserving the existing Debian behavior.
nullforge/molds/user.py-21-24 (1)

21-24: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Redact password from debug serialization.

UserMold inherits BaseMold.to_json() but does not declare password sensitive, exposing a configured password in pyinfra debug inventory. Add _sensitive_fields: ClassVar[tuple[str, ...]] = ("password",).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/user.py` around lines 21 - 24, Update UserMold to declare the
inherited sensitivity metadata by adding _sensitive_fields as a ClassVar tuple
containing "password", so BaseMold.to_json() redacts the password during debug
serialization.
nullforge/molds/containers.py-15-18 (1)

15-18: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject configuration values without a deployment implementation. Both molds accept feature values whose corresponding runes immediately raise, allowing validation to succeed before the cast fails.

  • nullforge/molds/containers.py#L15-L18: reject ContainersBackendType.CRIO while install=True, or implement CRI-O deployment.
  • nullforge/molds/dns.py#L16-L19: reject DnsMode.DOU, or implement the DNS-over-UDP deployment path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/containers.py` around lines 15 - 18, Reject unsupported
deployment configurations in the mold validators: in
nullforge/molds/containers.py:15-18, disallow ContainersBackendType.CRIO when
install=True unless its deployment rune is implemented; in
nullforge/molds/dns.py:16-19, disallow DnsMode.DOU unless the DNS-over-UDP
deployment path is implemented. Ensure validation fails before casting reaches
the unsupported runes.
nullforge/molds/monitoring/nezha.py-83-89 (1)

83-89: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not allow dashboard PATs over HTTP.

Line 87 accepts http://, while the deployment path sends api_token to dashboard_url for API calls. Require HTTPS so a valid remote configuration cannot transmit the PAT in cleartext.

Proposed fix
-        if v and not v.startswith(("https://", "http://")):
-            raise ValueError("dashboard_url must start with http:// or https://")
+        if v and not v.startswith("https://"):
+            raise ValueError("dashboard_url must start with https://")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/monitoring/nezha.py` around lines 83 - 89, Update
_validate_dashboard_url to reject any non-empty dashboard_url that does not use
https://, removing http:// from the accepted schemes while preserving
trailing-slash normalization and empty-value handling.

Source: Linters/SAST tools

nullforge/templates/systemd/dns-internal.service.j2-10-10 (1)

10-10: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Render IPv6-aware DNS listen endpoints.

listen_address accepts IPvAnyAddress, so dns-internal.service.j2:10 can render ip addr add <IPv6>/32 ..., and blocky.yaml.j2:8 can emit invalid ipv6:53 instead of a bracketed endpoint. Either enforce IPv4-only or make the templates family-aware for /32 vs IPv6 prefix and addr:53 vs [addr]:53.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/systemd/dns-internal.service.j2` at line 10, Update the
DNS endpoint templates to handle both IPv4 and IPv6 listen addresses: use the
appropriate address prefix length in the systemd service command and bracket
IPv6 addresses when rendering the Blocky DNS endpoint. Preserve the existing
IPv4 output and base the family-aware formatting on the configured
listen_address value.
nullforge/templates/dns/resolved.conf.j2-3-4 (1)

3-4: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Render DoT upstreams for resolved.conf

_deploy_dot_resolved resolves upstreams but only passes DOT/DOH to dns/resolved.conf.j2, so DNS= stays hard-coded to Cloudflare even when upstream_provider is Google or Quad9. Pass a formatted upstream list into the template (e.g., tcp-tls:[host]:port/host#sni equivalents) so the selected DNS provider and IPv6 setting affects this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/dns/resolved.conf.j2` around lines 3 - 4, Update
_deploy_dot_resolved to pass the resolved upstream list to dns/resolved.conf.j2,
formatting each DoT server as the template’s expected host#SNI or
tcp-tls:[host]:port representation and preserving IPv4/IPv6 handling. Replace
the hard-coded Cloudflare DNS= value in the template with the provided upstream
variable so upstream_provider and IPv6 configuration determine the rendered
resolvers.
nullforge/templates/systemd/cloudflare-tunnel.service.j2-12-14 (1)

12-14: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Pass the configured WARP interface to zt-tunnel-warp.sh.

WarpMold.iface is renderable from warp.py, but the Zero Trust systemd hooks invoke the script with only up/down, so zt-tunnel-warp.sh falls back to warp even when the deployed WARP interface is customized. Add and pass the iface argument here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/systemd/cloudflare-tunnel.service.j2` around lines 12 -
14, Update the ROUTE_THROUGH_WARP systemd hooks in the service template to pass
the configured WarpMold.iface value to zt-tunnel-warp.sh for both the up and
down actions, preserving the existing WORKDIR and action arguments.
nullforge/templates/cloudflared/tunnel.yml.j2-1-3 (1)

1-3: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Render the supplied POST_QUANTUM option.

_deploy_tunnel() passes POST_QUANTUM=opts.post_quantum, but nullforge/templates/cloudflared/tunnel.yml.j2 only emits token, protocol, and ha-connections, so enabling strict post-quantum mode is silently ignored. Cloudflare supports this in cloudflared tunnel run configuration as post-quantum: true.

Proposed fix
 token: {{ TOKEN }}
 protocol: {{ PROTOCOL }}
 ha-connections: {{ HA_CONNECTIONS }}
+post-quantum: {{ POST_QUANTUM }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/cloudflared/tunnel.yml.j2` around lines 1 - 3, Update the
cloudflared tunnel template to emit the supplied POST_QUANTUM value using the
post-quantum configuration key, alongside token, protocol, and ha-connections,
so _deploy_tunnel()’s opts.post_quantum setting is applied.
nullforge/templates/systemd/cloudflare-tunnel.service.j2-3-4 (1)

3-4: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Add systemd ordering/dependency for the WARP service.

When ROUTE_THROUGH_WARP is enabled, cloudflare-tunnel.service runs zt-tunnel-warp.sh immediately after cloudflared starts, but that script only replaces routes via its WARP interface and does not wait for/start the WARP unit. Add the appropriate WARP unit to both Requires=/Wants= and After= in cloudflare-tunnel.service.j2, or make the hook wait/retry until the configured interface is ready.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/systemd/cloudflare-tunnel.service.j2` around lines 3 - 4,
Add the configured WARP systemd unit to the cloudflare-tunnel service
dependencies by including it in both Wants/Requires and After alongside
network-online.target. Preserve the existing network dependency and ensure the
WARP unit is started and ordered before cloudflared runs zt-tunnel-warp.sh.
nullforge/templates/systemd/telemt.service.j2-17-30 (1)

17-30: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not grant CAP_NET_ADMIN to the proxy daemon.

The privileged ExecStartPre=+ helpers configure routing and iptables rules before the daemon starts; the long-running Telemt process should inherit only CAP_NET_BIND_SERVICE unless Telemt itself changes host network state after startup.

Proposed fix
-AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
-CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/systemd/telemt.service.j2` around lines 17 - 30, Remove
CAP_NET_ADMIN from the telemt.service.j2 AmbientCapabilities and
CapabilityBoundingSet declarations, retaining only CAP_NET_BIND_SERVICE for the
long-running proxy daemon. Leave the privileged ExecStartPre and ExecStopPost
helper commands unchanged.
nullforge/templates/scripts/warp-v6-policy.sh-16-38 (1)

16-38: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Prevent generated table ID and rule preference collisions.

_slot() maps interface names into 100 slots, so different interfaces can get the same TID/TABLE and rule PRIO. In the up path this can let one interface add rules/routes into another interface’s shared routing-table ID, and down can flush that table with ip -6 route flush table "$TABLE" even though the name differs. Use unique persisted IDs/prefs or fail before applying rules when either value is already owned by another interface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/scripts/warp-v6-policy.sh` around lines 16 - 38, Replace
the 100-slot hash-based allocation in _slot, TID, and PRIO with collision-safe
persisted ownership or validation before applying rules. Ensure each interface’s
table ID and rule preference are unique, and make the up path fail before adding
rules/routes when either value is owned by another interface; preserve explicit
TID/PRIO handling while validating those values as well.
nullforge/molds/utils.py-26-46 (1)

26-46: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Partial-dict layers should not reset unspecified fields to model defaults.

In _to_features_fragment and _to_system_dict, a dict layer is validated against the full mold, so missing fields are populated with class defaults. merge_* then deep-merges that fully populated fragment, overwriting existing values for fields the caller did not set in that layer.

🛠️ Proposed fix
         case Mapping():
             prepared = {k: (v.model_dump() if isinstance(v, ALLOWED_FEATURES_LAYERS) else v) for k, v in value.items()}
-            return FeaturesMold.model_validate(prepared).model_dump()
+            return FeaturesMold.model_validate(prepared).model_dump(exclude_unset=True)
         case Mapping():
-            return SystemMold.model_validate(value).model_dump()
+            return SystemMold.model_validate(value).model_dump(exclude_unset=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/utils.py` around lines 26 - 46, Update _to_features_fragment
and _to_system_dict so mapping layers preserve only fields explicitly supplied
by the caller. Avoid validating partial dictionaries through the full mold in a
way that populates default values; validate supplied values while retaining the
partial shape, then let merge_* deep-merge without overwriting unspecified
existing fields.
nullforge/models/dns.py-15-19 (1)

15-19: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not expose a mode that always aborts deployment.

DnsMode.DOU is accepted here, but nullforge/runes/dns.py:45-46 unconditionally raises for it. Remove it until implemented, or reject it during mold validation before a host run begins.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/models/dns.py` around lines 15 - 19, Remove DnsMode.DOU from the
DnsMode enum until its deployment behavior is implemented, or add mold
validation that rejects it before any host run begins; ensure the unsupported
mode cannot reach the unconditional failure in the DNS rune.
nullforge/models/monitoring/__init__.py-3-12 (1)

3-12: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the discriminator from the single-variant alias.

MonitoringLayout aliases one concrete model, but Field(discriminator="type") makes Pydantic treat it like the root of a discriminated union. If it is used in a Pydantic model, schema generation can fail before construction; extend this to a union when another layout is added.

Proposed fix
-from typing import Annotated
-
-from pydantic import Field
-
 from .base import MonitoringBackendType, _MonitoringLayoutBase
 from .nezha import NezhaLayout

-MonitoringLayout = Annotated[NezhaLayout, Field(discriminator="type")]
+MonitoringLayout = NezhaLayout
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/models/monitoring/__init__.py` around lines 3 - 12, Update the
MonitoringLayout alias to reference NezhaLayout directly without Annotated or
Field(discriminator="type"), while preserving the existing alias name and its
future-union intent. Remove any now-unused typing or Pydantic imports from this
module.
nullforge/smithy/install.py-123-148 (1)

123-148: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Predictable /tmp paths for root-privileged operations (symlink/TOCTOU, CWE-377) in two places. Both sites build fixed, guessable filenames under world-writable /tmp and then perform root-privileged filesystem operations against them, letting a local unprivileged user pre-plant a symlink to redirect root's writes/deletes/execution.

  • nullforge/smithy/install.py#L123-L148: download_path/workdir are static (/tmp/{name}, /tmp/nullforge-extract/{binary_name}); root then rm -rf/mkdir -p/extracts/installs through them. Add a random per-run token to both paths (e.g. secrets.token_hex(8)) or generate them via mktemp -d.
  • nullforge/smithy/monitoring/nezha/agent.py#L21-L54: script_path = "/tmp/nezha-agent-install.sh" is downloaded via curl -o and then directly executed as root — worse than the install.py case since the redirected content is executed, not just moved. Replace with a mktemp-generated path inline in the returned shell one-liner.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/install.py` around lines 123 - 148, The predictable
temporary paths in nullforge/smithy/install.py lines 123-148 must be replaced
with per-run unpredictable paths for both download_path and workdir, using a
secure random token or mktemp while preserving extraction, installation, and
cleanup behavior. In nullforge/smithy/monitoring/nezha/agent.py lines 21-54,
replace the fixed script_path with a path generated by mktemp inline in the
returned root shell command, ensuring the downloaded script is executed and
cleaned up through that unique path.

Source: Linters/SAST tools

nullforge/runes/users.py-57-68 (1)

57-68: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don’t pass the user password as a shell command argument.

The password is embedded in the printf ... | chpasswd string passed through server.shell(), so it can appear in process command-line arguments while the command runs. Avoid calling _set_user_password() this way; use a safer input path or pre-computed hashed password support rather than passing the plaintext value as an argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/runes/users.py` around lines 57 - 68, Update _set_user_password so
the plaintext password is not embedded in the shell command string or exposed as
a process argument. Use a safe input mechanism or existing precomputed-hash
support to provide the password to chpasswd, while preserving the user-targeting
behavior and password-setting operation.
nullforge/runes/xray.py-63-71 (1)

63-71: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add _sudo=True to the GeoIP/GeoSite downloads.

files.download writes into /usr/local/share/xray, but without _sudo=True the operation does not use privilege escalation. Run these downloads with the elevated install flow like the gVisor binaries so they don’t fail as the connecting user.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/runes/xray.py` around lines 63 - 71, Update the files.download call
in the GEO_DATS loop to pass _sudo=True, matching the elevated installation flow
used for gVisor binaries while preserving the existing download arguments.
🟡 Minor comments (8)
nullforge/molds/tor.py-13-20 (1)

13-20: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate Tor listener port ranges.

These values are written directly to torrc, but invalid ports (for example -1 or 65536) are accepted until deployment. Add appropriate bounds, such as ge=1, le=65535, unless port 0 is intentionally supported as a disable mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/tor.py` around lines 13 - 20, Add validation bounds to the
socks_port and dns_port fields in the Tor configuration model, limiting both
values to the valid listener range of 1 through 65535. Preserve their existing
defaults and descriptions, and do not allow zero unless the surrounding
configuration explicitly uses it to disable the listener.
nullforge/molds/telemt.py-117-128 (1)

117-128: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require a full username match.

_USERNAME_RE.match(name) accepts names with a trailing newline ("alice\n" passes), but usernames are rendered under [access.users] and break the TOML key. Use _USERNAME_RE.fullmatch(name) or \Z in the pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/telemt.py` around lines 117 - 128, The _validate_users
validator currently permits usernames with trailing newlines because
_USERNAME_RE.match only checks the prefix. Replace this check with a full-name
validation using _USERNAME_RE.fullmatch, while preserving the existing
invalid-username error and normalization behavior.
nullforge/molds/zerotrust.py-15-18 (1)

15-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize the tunnel token before validating it.

A whitespace-only token passes not self.token and reaches the generated tunnel configuration, where authentication fails. Strip the value before this validator runs.

Proposed fix
-from pydantic import Field, model_validator
+from pydantic import Field, field_validator, model_validator

 class ZeroTrustTunnelMold(BaseMold):
     token: str | None = Field(
         default=None,
         description="Tunnel token for authentication",
     )

+    `@field_validator`("token")
+    `@classmethod`
+    def _strip_token(cls, value: str | None) -> str | None:
+        return value.strip() if value is not None else None
+
     `@model_validator`(mode="after")

Also applies to: 42-45

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/molds/zerotrust.py` around lines 15 - 18, Normalize the token value
in the zerotrust model before validation so whitespace-only input is treated as
empty. Update the token field validation flow around the token field and its
validator to strip surrounding whitespace before applying the existing
required-token check, preserving valid non-whitespace token values.
nullforge/templates/scripts/zt-tunnel-warp.sh-19-23 (1)

19-23: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Make route cleanup idempotent.

ExecStopPost invokes down during service stops/restarts, but ip route del exits non-zero when a route is already absent. With set -e, the first missing route aborts the loop and later routes are not processed. systemd runs ExecStopPost during service shutdown and restart paths, so this hook must tolerate already-clean state. (man7.org)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/scripts/zt-tunnel-warp.sh` around lines 19 - 23, Update
the down) cleanup loop in zt-tunnel-warp.sh so an absent route does not cause ip
route del to fail the script under set -e. Allow each deletion to tolerate an
already-missing route while continuing to process all remaining CIDRS and retain
the existing removal logging.
nullforge/templates/profiles/tmux.conf-2-2 (1)

2-2: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the escaped quotes from terminal-overrides.

The backslash escapes make the quotes literal at runtime, so tmux does not see the standard ",xterm*":Tc" value and the xterm*` terminal pattern is not matched.

Proposed fix
-set-option -sa terminal-overrides \",xterm*:Tc\"
+set-option -sa terminal-overrides ",xterm*:Tc"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/templates/profiles/tmux.conf` at line 2, Update the
terminal-overrides set-option in the tmux configuration to remove the backslash
escapes around the quoted value, preserving the standard xterm* true-color
override format so tmux parses and matches the terminal pattern correctly.
nullforge/runes/profiles.py-24-46 (1)

24-46: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

_install_starship runs once per profile target instead of once per host.

_install_starship provisions a system-wide binary, but it's called from _install_user_profiles, which deploy_shell_profiles invokes once per target returned by _get_profile_targets (root and/or the named user). When both targets are enabled, starship's install ops are emitted twice for the same host — wasted duplicate work, and with reinstall=True it re-runs the install script twice. Every other system-wide installer (_install_eza, _install_tmux, _install_nvim, _install_zoxide, _install_direnv) is correctly called once outside the loop in deploy_shell_profiles.

🔧 Proposed fix
     _install_zoxide(reinstall)

     _install_direnv(reinstall)
+
+    _install_starship(reinstall)

     for user, home_dir in host.loop(_get_profile_targets(features)):
         _install_user_profiles(user, home_dir, reinstall, profiles_opts.font)
 def _install_user_profiles(user: str, home_dir: str, reinstall: bool, font: NerdFont | None) -> None:
     """Configure user profile."""

     _configure_user_oh_my_zsh(user, home_dir, reinstall)
     _configure_user_shell_profiles(user, home_dir)
     if font:
         _install_nerd_font(user, home_dir, font, reinstall)
     _install_user_tmux(user, home_dir, reinstall)
     _install_user_nvim(user, home_dir, reinstall)
     _install_atuin(user, home_dir, reinstall)
-    _install_starship(reinstall)

Also applies to: 64-75, 350-361

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/runes/profiles.py` around lines 24 - 46, Move the _install_starship
invocation out of _install_user_profiles and call it once from
deploy_shell_profiles alongside the other system-wide installers, before
iterating over _get_profile_targets(features). Remove the per-target call while
preserving user-specific profile setup for each loop target.
nullforge/smithy/versions.py-256-272 (1)

256-272: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

is_pinned_version_installed can implicitly return None instead of bool.

If suppress(FactError, FactProcessError) swallows an exception, execution falls off the end of the function with no return, giving None despite the -> bool annotation. Harmless in today's if/not call sites (falsy), but violates the type contract and is a latent footgun for future callers doing strict boolean checks.

🐛 Proposed fix
     with suppress(FactError, FactProcessError):
         output = _ctx_host.get_fact(Command, f"{command.format(bin=binary_path)} 2>&1 || true") or ""
         return pinned.removeprefix("v") in output
+    return False
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/versions.py` around lines 256 - 272, Update
is_pinned_version_installed so exceptions swallowed by the suppress(FactError,
FactProcessError) block produce an explicit False result instead of falling
through. Preserve the existing version-match return behavior for successful
command lookups and ensure every execution path returns a bool.
nullforge/smithy/admin.py-22-30 (1)

22-30: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Per-file ACLs are skipped whenever the directory ACL already exists.

The user:{username}:rwx not in acl_output guard gates the entire cmds list, including the rw_files loop. On a re-run where the directory ACL is already present but a file in rw_files was newly added or recreated (e.g. haproxy.cfg regenerated), its ACL won't be (re)applied, so the configured user silently loses access. Consider evaluating file ACLs independently of the directory-ACL check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/admin.py` around lines 22 - 30, The ACL guard in the smithy
ACL setup currently suppresses the rw_files loop when the directory ACL already
exists. Separate directory ACL commands from per-file ACL commands so each file
in rw_files is evaluated and its user:username:rw ACL is applied independently,
while preserving the existing directory ACL check and sudo shell execution.
🧹 Nitpick comments (2)
nullforge/smithy/install.py (1)

107-107: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Silent checksum-verification downgrade isn't logged.

When verify=True but sha256_for_download_url can't resolve a checksum (API unreachable/rate-limited), checksum becomes None and the download proceeds unverified with no warning. Consider logging when integrity verification is skipped so operators notice the degraded security posture.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/install.py` at line 107, Update the checksum selection flow
around sha256_for_download_url so that when verify is true but no explicit
sha256 is provided and checksum resolution returns None, log a warning that
integrity verification is being skipped before proceeding. Preserve existing
checksum behavior for resolved or explicitly supplied checksums.
nullforge/runes/profiles.py (1)

122-150: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify open() + io.StringIO + files.put to passing the template path directly.

Elsewhere in this codebase (e.g. nullforge/runes/telemt.py's _deploy_teleproxy_script, nullforge/runes/zerotrust.py's WARP-routing script deploy), files.put(src=get_template_path(...), ...) is passed the path string directly. Here, three call sites open the file, read it into memory, and wrap it in io.StringIO before handing it to files.put/files.block — unnecessary indirection for static (non-Jinja) files.

♻️ Example simplification (starship config)
-    with open(get_template_path("profiles/starship.toml"), encoding="utf-8") as f:
-        starship_config = f.read()
-
     files.put(
         name=f"Configure starship prompt for {user}",
-        src=io.StringIO(starship_config),
+        src=get_template_path("profiles/starship.toml"),
         dest=f"{home_dir}/.config/starship.toml",
         mode="0644",
         _sudo=True,
         _sudo_user=user,
     )

The same simplification applies to the direnv.toml put (lines 139-150) and the tmux.conf put (lines 285-296). (The nvim_patch.lua.j2 read at lines 328-330 is a different case since files.block only accepts content=, not src=, so that one must stay as-is.)

Also applies to: 285-296, 328-330

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/runes/profiles.py` around lines 122 - 150, Update
_configure_user_shell_profiles and the tmux configuration deployment to pass
each static template path directly as files.put’s src, removing the open(),
read(), and io.StringIO wrappers and their now-unused imports. Leave the
nvim_patch.lua.j2 handling unchanged because files.block requires content rather
than src.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nullforge/runes/base.py`:
- Around line 272-283: Initialize grub_contents to an empty collection before
the suppress block in _ensure_ipv6_stack, then retain the existing fact-fetch
assignment and subsequent any check. This ensures FactError or FactProcessError
leaves the function with no matching lines instead of referencing an unbound
local.

In `@nullforge/runes/netsec.py`:
- Around line 37-42: Initialize contents to an empty list before the suppress
block in _read_checksum_file, so suppressed FactError or FactProcessError
exceptions return an empty string instead of reaching the final expression with
contents unbound.

In `@nullforge/runes/users.py`:
- Around line 21-43: Update the password configuration branch in
deploy_user_management so _configure_passwordless_sudo is called only when
user_opts.sudo is true and no password is provided; users with sudo disabled
must not receive passwordless sudo configuration.

---

Major comments:
In `@nullforge/models/dns.py`:
- Around line 15-19: Remove DnsMode.DOU from the DnsMode enum until its
deployment behavior is implemented, or add mold validation that rejects it
before any host run begins; ensure the unsupported mode cannot reach the
unconditional failure in the DNS rune.

In `@nullforge/models/monitoring/__init__.py`:
- Around line 3-12: Update the MonitoringLayout alias to reference NezhaLayout
directly without Annotated or Field(discriminator="type"), while preserving the
existing alias name and its future-union intent. Remove any now-unused typing or
Pydantic imports from this module.

In `@nullforge/molds/containers.py`:
- Around line 15-18: Reject unsupported deployment configurations in the mold
validators: in nullforge/molds/containers.py:15-18, disallow
ContainersBackendType.CRIO when install=True unless its deployment rune is
implemented; in nullforge/molds/dns.py:16-19, disallow DnsMode.DOU unless the
DNS-over-UDP deployment path is implemented. Ensure validation fails before
casting reaches the unsupported runes.

In `@nullforge/molds/haproxy.py`:
- Around line 13-16: Update the HAProxy version handling across the model field
`version` and the provisioning logic in `haproxy` so RHEL does not silently
ignore a requested version: either install the selected version on RHEL or
validate the platform and reject/document version pinning as Debian-only, while
preserving the existing Debian behavior.

In `@nullforge/molds/monitoring/nezha.py`:
- Around line 83-89: Update _validate_dashboard_url to reject any non-empty
dashboard_url that does not use https://, removing http:// from the accepted
schemes while preserving trailing-slash normalization and empty-value handling.

In `@nullforge/molds/profiles.py`:
- Around line 11-14: Update the profile installation condition in the profiles
handling logic around user_opts.manage so the root profile entry is added
whenever profiles_opts.for_root is true, independently of managed-user creation;
preserve the existing managed-user handling separately.

In `@nullforge/molds/user.py`:
- Around line 21-24: Update UserMold to declare the inherited sensitivity
metadata by adding _sensitive_fields as a ClassVar tuple containing "password",
so BaseMold.to_json() redacts the password during debug serialization.

In `@nullforge/molds/utils.py`:
- Around line 26-46: Update _to_features_fragment and _to_system_dict so mapping
layers preserve only fields explicitly supplied by the caller. Avoid validating
partial dictionaries through the full mold in a way that populates default
values; validate supplied values while retaining the partial shape, then let
merge_* deep-merge without overwriting unspecified existing fields.

In `@nullforge/runes/users.py`:
- Around line 57-68: Update _set_user_password so the plaintext password is not
embedded in the shell command string or exposed as a process argument. Use a
safe input mechanism or existing precomputed-hash support to provide the
password to chpasswd, while preserving the user-targeting behavior and
password-setting operation.

In `@nullforge/runes/xray.py`:
- Around line 63-71: Update the files.download call in the GEO_DATS loop to pass
_sudo=True, matching the elevated installation flow used for gVisor binaries
while preserving the existing download arguments.

In `@nullforge/smithy/install.py`:
- Around line 123-148: The predictable temporary paths in
nullforge/smithy/install.py lines 123-148 must be replaced with per-run
unpredictable paths for both download_path and workdir, using a secure random
token or mktemp while preserving extraction, installation, and cleanup behavior.
In nullforge/smithy/monitoring/nezha/agent.py lines 21-54, replace the fixed
script_path with a path generated by mktemp inline in the returned root shell
command, ensuring the downloaded script is executed and cleaned up through that
unique path.

In `@nullforge/templates/cloudflared/tunnel.yml.j2`:
- Around line 1-3: Update the cloudflared tunnel template to emit the supplied
POST_QUANTUM value using the post-quantum configuration key, alongside token,
protocol, and ha-connections, so _deploy_tunnel()’s opts.post_quantum setting is
applied.

In `@nullforge/templates/dns/resolved.conf.j2`:
- Around line 3-4: Update _deploy_dot_resolved to pass the resolved upstream
list to dns/resolved.conf.j2, formatting each DoT server as the template’s
expected host#SNI or tcp-tls:[host]:port representation and preserving IPv4/IPv6
handling. Replace the hard-coded Cloudflare DNS= value in the template with the
provided upstream variable so upstream_provider and IPv6 configuration determine
the rendered resolvers.

In `@nullforge/templates/scripts/warp-v6-policy.sh`:
- Around line 16-38: Replace the 100-slot hash-based allocation in _slot, TID,
and PRIO with collision-safe persisted ownership or validation before applying
rules. Ensure each interface’s table ID and rule preference are unique, and make
the up path fail before adding rules/routes when either value is owned by
another interface; preserve explicit TID/PRIO handling while validating those
values as well.

In `@nullforge/templates/systemd/cloudflare-tunnel.service.j2`:
- Around line 12-14: Update the ROUTE_THROUGH_WARP systemd hooks in the service
template to pass the configured WarpMold.iface value to zt-tunnel-warp.sh for
both the up and down actions, preserving the existing WORKDIR and action
arguments.
- Around line 3-4: Add the configured WARP systemd unit to the cloudflare-tunnel
service dependencies by including it in both Wants/Requires and After alongside
network-online.target. Preserve the existing network dependency and ensure the
WARP unit is started and ordered before cloudflared runs zt-tunnel-warp.sh.

In `@nullforge/templates/systemd/dns-internal.service.j2`:
- Line 10: Update the DNS endpoint templates to handle both IPv4 and IPv6 listen
addresses: use the appropriate address prefix length in the systemd service
command and bracket IPv6 addresses when rendering the Blocky DNS endpoint.
Preserve the existing IPv4 output and base the family-aware formatting on the
configured listen_address value.

In `@nullforge/templates/systemd/telemt.service.j2`:
- Around line 17-30: Remove CAP_NET_ADMIN from the telemt.service.j2
AmbientCapabilities and CapabilityBoundingSet declarations, retaining only
CAP_NET_BIND_SERVICE for the long-running proxy daemon. Leave the privileged
ExecStartPre and ExecStopPost helper commands unchanged.

---

Minor comments:
In `@nullforge/molds/telemt.py`:
- Around line 117-128: The _validate_users validator currently permits usernames
with trailing newlines because _USERNAME_RE.match only checks the prefix.
Replace this check with a full-name validation using _USERNAME_RE.fullmatch,
while preserving the existing invalid-username error and normalization behavior.

In `@nullforge/molds/tor.py`:
- Around line 13-20: Add validation bounds to the socks_port and dns_port fields
in the Tor configuration model, limiting both values to the valid listener range
of 1 through 65535. Preserve their existing defaults and descriptions, and do
not allow zero unless the surrounding configuration explicitly uses it to
disable the listener.

In `@nullforge/molds/zerotrust.py`:
- Around line 15-18: Normalize the token value in the zerotrust model before
validation so whitespace-only input is treated as empty. Update the token field
validation flow around the token field and its validator to strip surrounding
whitespace before applying the existing required-token check, preserving valid
non-whitespace token values.

In `@nullforge/runes/profiles.py`:
- Around line 24-46: Move the _install_starship invocation out of
_install_user_profiles and call it once from deploy_shell_profiles alongside the
other system-wide installers, before iterating over
_get_profile_targets(features). Remove the per-target call while preserving
user-specific profile setup for each loop target.

In `@nullforge/smithy/admin.py`:
- Around line 22-30: The ACL guard in the smithy ACL setup currently suppresses
the rw_files loop when the directory ACL already exists. Separate directory ACL
commands from per-file ACL commands so each file in rw_files is evaluated and
its user:username:rw ACL is applied independently, while preserving the existing
directory ACL check and sudo shell execution.

In `@nullforge/smithy/versions.py`:
- Around line 256-272: Update is_pinned_version_installed so exceptions
swallowed by the suppress(FactError, FactProcessError) block produce an explicit
False result instead of falling through. Preserve the existing version-match
return behavior for successful command lookups and ensure every execution path
returns a bool.

In `@nullforge/templates/profiles/tmux.conf`:
- Line 2: Update the terminal-overrides set-option in the tmux configuration to
remove the backslash escapes around the quoted value, preserving the standard
xterm* true-color override format so tmux parses and matches the terminal
pattern correctly.

In `@nullforge/templates/scripts/zt-tunnel-warp.sh`:
- Around line 19-23: Update the down) cleanup loop in zt-tunnel-warp.sh so an
absent route does not cause ip route del to fail the script under set -e. Allow
each deletion to tolerate an already-missing route while continuing to process
all remaining CIDRS and retain the existing removal logging.

---

Nitpick comments:
In `@nullforge/runes/profiles.py`:
- Around line 122-150: Update _configure_user_shell_profiles and the tmux
configuration deployment to pass each static template path directly as
files.put’s src, removing the open(), read(), and io.StringIO wrappers and their
now-unused imports. Leave the nvim_patch.lua.j2 handling unchanged because
files.block requires content rather than src.

In `@nullforge/smithy/install.py`:
- Line 107: Update the checksum selection flow around sha256_for_download_url so
that when verify is true but no explicit sha256 is provided and checksum
resolution returns None, log a warning that integrity verification is being
skipped before proceeding. Preserve existing checksum behavior for resolved or
explicitly supplied checksums.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b01edc5-9e56-4298-b0bc-f07281e9dffc

📥 Commits

Reviewing files that changed from the base of the PR and between 9ebd4e0 and d44ecad.

📒 Files selected for processing (97)
  • .gitignore
  • README.md
  • nullforge/foundry/README.md
  • nullforge/foundry/__init__.py
  • nullforge/foundry/full_cast.py
  • nullforge/inventories/README.md
  • nullforge/inventories/example.py
  • nullforge/models/__init__.py
  • nullforge/models/containers.py
  • nullforge/models/dns.py
  • nullforge/models/monitoring/__init__.py
  • nullforge/models/monitoring/base.py
  • nullforge/models/monitoring/nezha.py
  • nullforge/models/netsec.py
  • nullforge/models/profiles.py
  • nullforge/models/system.py
  • nullforge/models/users.py
  • nullforge/models/warp.py
  • nullforge/models/zerotrust.py
  • nullforge/molds/__init__.py
  • nullforge/molds/base_mold.py
  • nullforge/molds/containers.py
  • nullforge/molds/defaults.py
  • nullforge/molds/dns.py
  • nullforge/molds/features.py
  • nullforge/molds/haproxy.py
  • nullforge/molds/monitoring/__init__.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/molds/netsec.py
  • nullforge/molds/profiles.py
  • nullforge/molds/system.py
  • nullforge/molds/telemt.py
  • nullforge/molds/tor.py
  • nullforge/molds/user.py
  • nullforge/molds/utils.py
  • nullforge/molds/warp.py
  • nullforge/molds/xray.py
  • nullforge/molds/zerotrust.py
  • nullforge/runes/__init__.py
  • nullforge/runes/base.py
  • nullforge/runes/containers.py
  • nullforge/runes/dns.py
  • nullforge/runes/haproxy.py
  • nullforge/runes/monitoring.py
  • nullforge/runes/netsec.py
  • nullforge/runes/prepare.py
  • nullforge/runes/profiles.py
  • nullforge/runes/telemt.py
  • nullforge/runes/tor.py
  • nullforge/runes/users.py
  • nullforge/runes/warp.py
  • nullforge/runes/xray.py
  • nullforge/runes/zerotrust.py
  • nullforge/smithy/__init__.py
  • nullforge/smithy/admin.py
  • nullforge/smithy/arch.py
  • nullforge/smithy/blocky.py
  • nullforge/smithy/cloudflare.py
  • nullforge/smithy/github.py
  • nullforge/smithy/http.py
  • nullforge/smithy/install.py
  • nullforge/smithy/monitoring/__init__.py
  • nullforge/smithy/monitoring/nezha/__init__.py
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/smithy/monitoring/nezha/dashboard.py
  • nullforge/smithy/monitoring/nezha/deploy.py
  • nullforge/smithy/network.py
  • nullforge/smithy/packages.py
  • nullforge/smithy/service.py
  • nullforge/smithy/sni.py
  • nullforge/smithy/swap.py
  • nullforge/smithy/system.py
  • nullforge/smithy/versions.py
  • nullforge/templates/__init__.py
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/templates/dns/blocky.yaml.j2
  • nullforge/templates/dns/dns.yaml.j2
  • nullforge/templates/dns/resolv.conf.j2
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/templates/etc/default/zramswap.j2
  • nullforge/templates/nvim/nvim_patch.lua.j2
  • nullforge/templates/profiles/direnv.toml
  • nullforge/templates/profiles/starship.toml
  • nullforge/templates/profiles/tmux.conf
  • nullforge/templates/profiles/zshrc.j2
  • nullforge/templates/scripts/telemt-synfix.sh
  • nullforge/templates/scripts/teleproxy-warp.sh
  • nullforge/templates/scripts/warp-v6-policy.sh
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/systemd/blocky.service.j2
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/systemd/cloudflare-warp.service.j2
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/templates/systemd/telemt.service.j2
  • nullforge/templates/telemt/telemt.toml.j2
  • nullforge/templates/tor/torrc.j2
  • pyproject.toml

Comment thread nullforge/runes/base.py
Comment thread nullforge/runes/netsec.py
Comment thread nullforge/runes/users.py Outdated
@wlix13
wlix13 force-pushed the feature/implement-runes branch from d44ecad to 2077b96 Compare July 24, 2026 19:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
nullforge/smithy/system.py (2)

45-75: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Treat whitespace-only preferred values as unset.

A truthy value such as " " has no first token, so direct first-token extraction can raise instead of falling through to the default locale order. Strip first and guard the empty result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/system.py` around lines 45 - 75, Update detect_best_locale
to normalize preferred with surrounding whitespace removed before checking or
extracting its locale name. Treat an empty normalized value as unset, avoiding
split()[0] and allowing the existing default locale selection to run.

11-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the memory cache key account for default.

When the fact is unavailable, the first call caches its fallback under a fixed key; a later call with another default returns the earlier value. Include default in the cache key or cache only actual fact values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/system.py` around lines 11 - 18, Update the memory caching
logic around the cache_key and get_fact(Memory) flow so calls with different
default values cannot reuse a fallback cached for another default. Include
default in the cache key, while preserving reuse of the computed total_mb and
existing behavior when the Memory fact is available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nullforge/molds/zerotrust.py`:
- Around line 57-58: Update the post-quantum validation in the relevant
ZeroTrust configuration initializer to require self.protocol ==
ZeroTrustTunnelProtocol.QUIC, rejecting AUTO and all other protocols while
preserving the existing ValueError message and behavior for valid QUIC
configuration.

In `@nullforge/smithy/system.py`:
- Around line 34-39: Update the locale parsing logic around the parts handling
to validate the locale and charmap tokens before appending to locales. Ensure
non-locale comments such as “This file ...” are rejected, while valid locale
entries continue to be appended unchanged.

---

Outside diff comments:
In `@nullforge/smithy/system.py`:
- Around line 45-75: Update detect_best_locale to normalize preferred with
surrounding whitespace removed before checking or extracting its locale name.
Treat an empty normalized value as unset, avoiding split()[0] and allowing the
existing default locale selection to run.
- Around line 11-18: Update the memory caching logic around the cache_key and
get_fact(Memory) flow so calls with different default values cannot reuse a
fallback cached for another default. Include default in the cache key, while
preserving reuse of the computed total_mb and existing behavior when the Memory
fact is available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b2d5003-19bb-46f1-90f9-ba5e1120d7cd

📥 Commits

Reviewing files that changed from the base of the PR and between d44ecad and 2077b96.

📒 Files selected for processing (31)
  • README.md
  • nullforge/molds/haproxy.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/molds/telemt.py
  • nullforge/molds/tor.py
  • nullforge/molds/user.py
  • nullforge/molds/utils.py
  • nullforge/molds/zerotrust.py
  • nullforge/runes/base.py
  • nullforge/runes/dns.py
  • nullforge/runes/haproxy.py
  • nullforge/runes/netsec.py
  • nullforge/runes/profiles.py
  • nullforge/runes/users.py
  • nullforge/runes/xray.py
  • nullforge/runes/zerotrust.py
  • nullforge/smithy/admin.py
  • nullforge/smithy/install.py
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/smithy/network.py
  • nullforge/smithy/system.py
  • nullforge/smithy/versions.py
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/templates/dns/blocky.yaml.j2
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/templates/profiles/tmux.conf
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/templates/systemd/telemt.service.j2
  • pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (26)
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/molds/haproxy.py
  • nullforge/templates/dns/blocky.yaml.j2
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/profiles/tmux.conf
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/templates/systemd/telemt.service.j2
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/runes/users.py
  • nullforge/smithy/network.py
  • nullforge/runes/haproxy.py
  • nullforge/smithy/admin.py
  • nullforge/runes/xray.py
  • nullforge/runes/dns.py
  • nullforge/molds/telemt.py
  • nullforge/runes/zerotrust.py
  • nullforge/molds/user.py
  • nullforge/smithy/versions.py
  • nullforge/smithy/install.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/runes/netsec.py
  • nullforge/runes/base.py
  • nullforge/runes/profiles.py
  • nullforge/molds/utils.py

Comment thread nullforge/molds/zerotrust.py
Comment thread nullforge/smithy/system.py Outdated
@wlix13
wlix13 force-pushed the feature/implement-runes branch 2 times, most recently from bbd9314 to 96d6115 Compare July 25, 2026 19:10
@github-actions github-actions Bot added the feature New feature or request label Jul 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
nullforge/smithy/monitoring/nezha/dashboard.py (1)

28-43: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider validating scheme inside _api_request rather than relying solely on the caller.

The # noqa: S310 - scheme validated by caller comment assumes upstream (mold) validation of dashboard_url's scheme. Since this is a reusable smithy helper, not gated to one call site, adding the same https://-only guard used in smithy/http.py's fetch_text would be a cheap, self-contained hardening that doesn't depend on that external assumption holding.

🛡️ Suggested guard
 def _api_request(
     url: str,
     token: str,
     *,
     method: str = "GET",
     payload: dict[str, object] | None = None,
 ) -> dict[str, Any]:
     """Issue authenticated dashboard API request and return parsed envelope."""
+
+    if not url.startswith("https://"):
+        raise ValueError(f"Refusing non-HTTPS dashboard URL: {url}")

Please confirm whether dashboard_url's mold field already enforces an http(s)-only type, which would make this optional rather than necessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/smithy/monitoring/nezha/dashboard.py` around lines 28 - 43,
Validate the URL scheme inside `_api_request` before constructing the
`urllib.request.Request`, enforcing the same HTTPS-only guard used by
`fetch_text` in `smithy/http.py`. Remove the reliance on caller-side validation
and update the security suppression comment to reflect the local validation.
nullforge/runes/profiles.py (1)

365-365: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Predictable /tmp filenames for root-privileged downloads across multiple installers. All six sites download/install to a fixed, guessable path under /tmp, run with _sudo=True; on a host with other local users this is susceptible to symlink pre-creation/TOCTOU races (CWE-377). Same fix applies everywhere: generate the path via mktemp/mktemp -d instead of a hardcoded literal.

  • nullforge/runes/profiles.py#L365-L365: replace starship_install_path = "/tmp/starship.sh" with a mktemp-generated path.
  • nullforge/runes/profiles.py#L391-L391: replace atuin_install_path = "/tmp/atuin.sh" with a mktemp-generated path.
  • nullforge/runes/profiles.py#L417-L417: replace zoxide_install_path = "/tmp/zoxide.sh" with a mktemp-generated path.
  • nullforge/runes/profiles.py#L472-L473: replace tmux_tar_path/tmux_src_dir with mktemp/mktemp -d-generated paths.
  • nullforge/runes/profiles.py#L529-L529: replace nvim_appimage_path = "/tmp/nvim.appimage" with a mktemp-generated path.
  • nullforge/runes/containers.py#L148-L148: replace get_docker_path = "/tmp/get-docker.sh" with a mktemp-generated path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullforge/runes/profiles.py` at line 365, Replace the predictable
root-privileged temporary paths with securely generated temporary paths. In
nullforge/runes/profiles.py lines 365-365, 391-391, 417-417, 529-529, use
mktemp-generated file paths for the installer variables; at lines 472-473, use
mktemp for tmux_tar_path and mktemp -d for tmux_src_dir; in
nullforge/runes/containers.py line 148, apply the same mktemp-based path
generation to get_docker_path.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nullforge/runes/netsec.py`:
- Around line 191-236: Update the firewalld rule builder around action_map to
reject rules with action="limit" before any port or rich-rule commands are
generated, raising a clear ValueError like the existing direction="out"
rejection. Remove the limit-to-accept mapping so unsupported limit rules cannot
emit unrestricted allow commands; preserve behavior for allow, deny, and reject.

In `@nullforge/smithy/system.py`:
- Around line 25-26: Update the memory lookup in the surrounding system resource
function to tolerate Memory fact failures by requesting the fact with
_ignore_errors=True, while preserving the existing default fallback when no
value is returned; alternatively, replace the unreliable Memory lookup with
portable /proc/meminfo parsing.

---

Nitpick comments:
In `@nullforge/runes/profiles.py`:
- Line 365: Replace the predictable root-privileged temporary paths with
securely generated temporary paths. In nullforge/runes/profiles.py lines
365-365, 391-391, 417-417, 529-529, use mktemp-generated file paths for the
installer variables; at lines 472-473, use mktemp for tmux_tar_path and mktemp
-d for tmux_src_dir; in nullforge/runes/containers.py line 148, apply the same
mktemp-based path generation to get_docker_path.

In `@nullforge/smithy/monitoring/nezha/dashboard.py`:
- Around line 28-43: Validate the URL scheme inside `_api_request` before
constructing the `urllib.request.Request`, enforcing the same HTTPS-only guard
used by `fetch_text` in `smithy/http.py`. Remove the reliance on caller-side
validation and update the security suppression comment to reflect the local
validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1592e65d-2ecd-4abe-bc2d-8228698af966

📥 Commits

Reviewing files that changed from the base of the PR and between 2077b96 and 96d6115.

📒 Files selected for processing (97)
  • .gitignore
  • README.md
  • nullforge/foundry/README.md
  • nullforge/foundry/__init__.py
  • nullforge/foundry/full_cast.py
  • nullforge/inventories/README.md
  • nullforge/inventories/example.py
  • nullforge/models/__init__.py
  • nullforge/models/containers.py
  • nullforge/models/dns.py
  • nullforge/models/monitoring/__init__.py
  • nullforge/models/monitoring/base.py
  • nullforge/models/monitoring/nezha.py
  • nullforge/models/netsec.py
  • nullforge/models/profiles.py
  • nullforge/models/system.py
  • nullforge/models/users.py
  • nullforge/models/warp.py
  • nullforge/models/zerotrust.py
  • nullforge/molds/__init__.py
  • nullforge/molds/base_mold.py
  • nullforge/molds/containers.py
  • nullforge/molds/defaults.py
  • nullforge/molds/dns.py
  • nullforge/molds/features.py
  • nullforge/molds/haproxy.py
  • nullforge/molds/monitoring/__init__.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/molds/netsec.py
  • nullforge/molds/profiles.py
  • nullforge/molds/system.py
  • nullforge/molds/telemt.py
  • nullforge/molds/tor.py
  • nullforge/molds/user.py
  • nullforge/molds/utils.py
  • nullforge/molds/warp.py
  • nullforge/molds/xray.py
  • nullforge/molds/zerotrust.py
  • nullforge/runes/__init__.py
  • nullforge/runes/base.py
  • nullforge/runes/containers.py
  • nullforge/runes/dns.py
  • nullforge/runes/haproxy.py
  • nullforge/runes/monitoring.py
  • nullforge/runes/netsec.py
  • nullforge/runes/prepare.py
  • nullforge/runes/profiles.py
  • nullforge/runes/telemt.py
  • nullforge/runes/tor.py
  • nullforge/runes/users.py
  • nullforge/runes/warp.py
  • nullforge/runes/xray.py
  • nullforge/runes/zerotrust.py
  • nullforge/smithy/__init__.py
  • nullforge/smithy/admin.py
  • nullforge/smithy/arch.py
  • nullforge/smithy/blocky.py
  • nullforge/smithy/cloudflare.py
  • nullforge/smithy/github.py
  • nullforge/smithy/http.py
  • nullforge/smithy/install.py
  • nullforge/smithy/monitoring/__init__.py
  • nullforge/smithy/monitoring/nezha/__init__.py
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/smithy/monitoring/nezha/dashboard.py
  • nullforge/smithy/monitoring/nezha/deploy.py
  • nullforge/smithy/network.py
  • nullforge/smithy/packages.py
  • nullforge/smithy/service.py
  • nullforge/smithy/sni.py
  • nullforge/smithy/swap.py
  • nullforge/smithy/system.py
  • nullforge/smithy/versions.py
  • nullforge/templates/__init__.py
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/templates/dns/blocky.yaml.j2
  • nullforge/templates/dns/dns.yaml.j2
  • nullforge/templates/dns/resolv.conf.j2
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/templates/etc/default/zramswap.j2
  • nullforge/templates/nvim/nvim_patch.lua.j2
  • nullforge/templates/profiles/direnv.toml
  • nullforge/templates/profiles/starship.toml
  • nullforge/templates/profiles/tmux.conf
  • nullforge/templates/profiles/zshrc.j2
  • nullforge/templates/scripts/telemt-synfix.sh
  • nullforge/templates/scripts/teleproxy-warp.sh
  • nullforge/templates/scripts/warp-v6-policy.sh
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/systemd/blocky.service.j2
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/systemd/cloudflare-warp.service.j2
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/templates/systemd/telemt.service.j2
  • nullforge/templates/telemt/telemt.toml.j2
  • nullforge/templates/tor/torrc.j2
  • pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (82)
  • nullforge/templates/dns/resolv.conf.j2
  • nullforge/models/init.py
  • nullforge/templates/systemd/cloudflare-warp.service.j2
  • nullforge/models/monitoring/nezha.py
  • nullforge/smithy/init.py
  • nullforge/foundry/init.py
  • nullforge/models/users.py
  • nullforge/molds/haproxy.py
  • nullforge/templates/systemd/cloudflare-tunnel.service.j2
  • nullforge/templates/profiles/direnv.toml
  • nullforge/models/zerotrust.py
  • nullforge/smithy/monitoring/init.py
  • nullforge/models/netsec.py
  • nullforge/molds/tor.py
  • pyproject.toml
  • nullforge/runes/monitoring.py
  • nullforge/molds/containers.py
  • nullforge/smithy/service.py
  • nullforge/molds/xray.py
  • nullforge/runes/tor.py
  • nullforge/models/containers.py
  • nullforge/templates/systemd/dns-internal.service.j2
  • nullforge/foundry/full_cast.py
  • .gitignore
  • nullforge/molds/profiles.py
  • nullforge/molds/defaults.py
  • nullforge/templates/init.py
  • nullforge/models/monitoring/base.py
  • nullforge/models/system.py
  • nullforge/smithy/arch.py
  • nullforge/templates/tor/torrc.j2
  • nullforge/templates/cloudflared/tunnel.yml.j2
  • nullforge/smithy/cloudflare.py
  • nullforge/templates/dns/dns.yaml.j2
  • nullforge/molds/monitoring/init.py
  • nullforge/templates/systemd/blocky.service.j2
  • nullforge/runes/init.py
  • nullforge/models/profiles.py
  • nullforge/inventories/example.py
  • nullforge/templates/systemd/telemt.service.j2
  • nullforge/runes/xray.py
  • nullforge/smithy/blocky.py
  • nullforge/molds/zerotrust.py
  • nullforge/runes/prepare.py
  • nullforge/molds/dns.py
  • nullforge/templates/scripts/zt-tunnel-warp.sh
  • nullforge/templates/dns/resolved.conf.j2
  • nullforge/molds/init.py
  • nullforge/molds/system.py
  • nullforge/templates/profiles/tmux.conf
  • nullforge/models/warp.py
  • nullforge/templates/telemt/telemt.toml.j2
  • nullforge/runes/haproxy.py
  • nullforge/smithy/swap.py
  • nullforge/runes/users.py
  • nullforge/molds/features.py
  • nullforge/templates/profiles/zshrc.j2
  • nullforge/molds/user.py
  • nullforge/templates/etc/default/zramswap.j2
  • nullforge/models/dns.py
  • nullforge/smithy/monitoring/nezha/agent.py
  • nullforge/smithy/monitoring/nezha/deploy.py
  • nullforge/smithy/admin.py
  • nullforge/templates/profiles/starship.toml
  • nullforge/runes/zerotrust.py
  • nullforge/runes/telemt.py
  • nullforge/smithy/install.py
  • nullforge/molds/warp.py
  • nullforge/molds/netsec.py
  • nullforge/smithy/packages.py
  • nullforge/runes/dns.py
  • nullforge/molds/telemt.py
  • nullforge/molds/monitoring/nezha.py
  • nullforge/molds/utils.py
  • nullforge/smithy/versions.py
  • nullforge/molds/base_mold.py
  • nullforge/smithy/network.py
  • nullforge/runes/warp.py
  • nullforge/smithy/github.py
  • nullforge/smithy/sni.py
  • nullforge/templates/nvim/nvim_patch.lua.j2
  • nullforge/runes/base.py

Comment thread nullforge/runes/netsec.py
Comment thread nullforge/smithy/system.py
@wlix13
wlix13 force-pushed the feature/implement-runes branch from 96d6115 to 951f6d5 Compare July 25, 2026 19:46
@wlix13
wlix13 merged commit f2ad327 into main Jul 25, 2026
7 of 9 checks passed
@wlix13
wlix13 deleted the feature/implement-runes branch July 25, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation updates feature New feature or request foundry Foundry entrypoints and inventories molds Configuration molds and domain models runes Provisioning runes smithy Cross-distro helpers and shared provisioning logic templates Templates updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant