Skip to content
Open
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
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,22 @@ dist-obs/

# maturin develop artifact (built into the source tree)
python/ciris_server/_native*.so

# ── Node-home artifacts, if a run ever points CIRIS_HOME at the checkout ──────
#
# `qa/qa_runner/server.py` used to default CIRIS_HOME to the repo root, which made
# a node write its store, logs, secrets and — on a first run — the one-time
# `claim_pin` into the working tree. That default is fixed (per-run temp dir), and
# these stay as the belt: a claim SECRET must never become a tracked file, and a
# stray `ciris_engine.db` must never be committable by an absent-minded `git add`.
#
# `data/` is NOT listed: it holds tracked mesh fixtures. A node home would collide
# with it, which is a second reason home does not belong in the checkout.
/claim_pin
/ciris_engine.db
/ciris_engine.db-wal
/ciris_engine.db-shm
Comment on lines +37 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Ignore the database in its actual data directory

When CIRIS_HOME is pointed at the checkout—the exact fallback scenario these rules are meant to protect—the database is written to data/ciris_engine.db, not the repository root: ServerConfig::from_home sets data_dir = home.join("data") and db_path() appends ciris_engine.db (src/config.rs:204,251-252), which also matches server.py:574. Consequently these root-only patterns do not match the database or its WAL/SHM files, and git add can still stage the node store. Add specific /data/ciris_engine.db* exclusions while leaving the tracked mesh fixtures visible.

Useful? React with 👍 / 👎.

/logs/
/secrets/
/keys/
/identity/
35 changes: 31 additions & 4 deletions qa/qa_runner/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
API server management for QA testing.
"""

import tempfile
import asyncio
import hashlib
import json
Expand Down Expand Up @@ -829,11 +830,28 @@ def start(self) -> bool:
if not self.config.mock_llm:
env.pop("CIRIS_MOCK_LLM", None) # Remove if present

# Set CIRIS_HOME for verifier_singleton (required for audit hash chain)
# CIRIS_HOME for verifier_singleton (required for audit hash chain).
#
# NOT the repo root. Pointing home at the checkout makes the node write its
# data dir, logs, secrets, `ciris_engine.db` and — on a first run — the
# one-time `claim_pin` INTO the working tree. Two failures follow:
#
# 1. Anything that reads home at the PRODUCT default looks somewhere else
# and finds nothing. A first-run claim then fails with the PIN sitting
# in the repo, written by the same run that could not find it. This is
# the shape that just cost a desktop first-run setup (CIRISAgent's
# `server_manager` had the identical `setdefault(CIRIS_HOME, root)`).
# 2. A one-time claim SECRET lands in a git checkout.
#
# A per-run temp dir instead: isolated between runs, cleaned by the OS, and
# it cannot disagree with a product default because nothing else claims it.
# An explicit CIRIS_HOME from the caller still wins — that is the knob for
# pointing a run at a real home on purpose.
if "CIRIS_HOME" not in env:
project_root = Path(__file__).parent.parent.parent
env["CIRIS_HOME"] = str(project_root)
self.console.print(f"[dim]Setting CIRIS_HOME={project_root}[/dim]")
qa_home = Path(tempfile.mkdtemp(prefix="ciris-qa-home-"))
env["CIRIS_HOME"] = str(qa_home)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Point state validators at the per-run home

When the handler or CIRISNode modules run, this redirects the agent's state to <qa_home>/data, but the tests still inspect repository-relative paths: handler_tests.py:233 opens data/ciris_audit.db for every handler audit assertion, and cirisnode_tests.py:248 checks data/agent_signing.key. Those checks therefore report missing files even though the server created them successfully in the temporary home. Expose the selected home to these validators or update them to resolve paths through CIRIS_HOME as part of this change.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reuse the temporary home across identity-update restarts

When the standalone identity_update module runs, it kills the manager-owned server and starts a replacement at identity_update_tests.py:246-298 using {**os.environ, "CIRIS_CONFIGURED": "true"}. The temporary CIRIS_HOME exists only in this manager's private child environment and is never added to os.environ, so the replacement cannot locate the database and identity created by the original process; the post-restart login at lines 342-362 consequently targets a different node state and fails. Make the selected home available to restart workflows or have this module restart through the manager with the same environment.

Useful? React with 👍 / 👎.

self._qa_home = qa_home
self.console.print(f"[dim]Setting CIRIS_HOME={qa_home} (per-run temp)[/dim]")

# Set CIRIS_ADAPTER environment variable (supports comma-separated adapters)
# This allows loading modular services like Reddit alongside built-in adapters
Expand Down Expand Up @@ -1186,6 +1204,15 @@ def stop(self):
self.process = None
self.pid = None

# Remove the per-run temp home, but ONLY one we created. A home
# supplied by the caller is theirs and may hold a real node.
qa_home = getattr(self, "_qa_home", None)
if qa_home is not None:
import shutil as _sh

_sh.rmtree(qa_home, ignore_errors=True)
Comment on lines +1209 to +1213

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clean the temporary home after failed starts

If startup raises after mkdtemp()—for example, Popen fails in staged mode, or opening the console log fails after the child starts—the exception handler at start():1182 returns False without calling stop(). Moreover, this cleanup is nested under if self.process, so even a caller that invokes stop() cannot remove a home when failure occurred before process assignment. Such failures leave per-run directories behind, and post-spawn failures can leave the database and claim PIN in them; move home cleanup outside the process conditional and run it on the startup exception path.

Useful? React with 👍 / 👎.

self._qa_home = None

# Close PTY master fd
if hasattr(self, "_pty_master_fd"):
try:
Expand Down
Loading