# Archify CLI Safe Output Path Resolution: How It Prevents Path Traversal and Overwrites

> Archify CLI's safe output path resolution prevents path traversal and overwrites by validating paths against your working directory and repository root. Learn how.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: deep-dive
- Published: 2026-08-17

---

**Archify validates every output path by resolving it against the current working directory, enforcing HTML-only file extensions, and containing writes within the repository root using `pathIsInside()` checks.**

The Archify CLI implements a multi-layered safety system to ensure generated artifacts are written only to permitted locations. According to the `tt-a1i/archify` source code, this protection is coordinated across three core modules: the CLI driver, the output-path renderer, and the guard management system.

## How Output Paths Are Resolved and Validated

When a user invokes `archify render`, the raw output value from CLI arguments passes through a strict normalization and validation pipeline in `archify/renderers/shared/output-path.mjs`.

### Step 1: Absolute Path Resolution

The renderer first converts any relative path to an absolute location using Node.js `path.resolve()`:

```javascript
// L282 in output-path.mjs
const outputPath = path.resolve(cwd, rawOutput);

```

This ensures consistent path handling regardless of how the user specified the target.

### Step 2: File Type Enforcement

If the render request originates from a **meta source** (a `.archify` description file), the renderer mandates that the output filename end with `.html`:

```javascript
// L289 in output-path.mjs
if (sourceType === 'meta' && !outputPath.endsWith('.html')) {
  throw createDiagnostic('invalid_output_extension', { expected: '.html' });
}

```

This prevents accidental generation of unrendered or potentially dangerous file types.

### Step 3: Repository Containment Check

The critical security boundary is enforced by `pathIsInside()`:

```javascript
// L289-L290 in output-path.mjs
if (!pathIsInside(cwd, outputPath)) {
  throw createDiagnostic('unsafe_output_path', {
    hint: 'choose a safe output path and retry'
  });
}

```

Any path that would escape the working directory—such as `../../etc/passwd` or `/absolute/path`—triggers an immediate rejection with a diagnostic containing a **supported fix** hint.

## Duplicate Prevention and Conflict Resolution

Output-path clashes are detected by scanning pending writes against `otherOutputPath` entries:

```javascript
// L298-L312 in output-path.mjs
for (const pending of otherOutputPath) {
  if (isConflict(outputPath, pending.resolvedPath)) {
    throw createDiagnostic('output_path_collision', {
      existing: pending.sourceFile
    });
  }
}

```

This prevents one architecture diagram from silently overwriting another when multiple inputs target the same destination.

## Concurrent Write Protection with Guard Maps

The `archify/renderers/shared/cli.mjs` module implements atomic claim tracking through an `outputPathGuards` map (L13-L68):

```javascript
// Conceptual guard implementation
const outputPathGuards = new Map();

function claimOutputPath(resolvedPath, guardToken) {
  if (outputPathGuards.has(resolvedPath)) {
    throw createDiagnostic('concurrent_output_conflict');
  }
  outputPathGuards.set(resolvedPath, guardToken);
  return () => outputPathGuards.delete(resolvedPath); // release function
}

```

This mechanism ensures **race-free writes** even when Archify is invoked concurrently by different processes or async operations.

## CLI Error Handling and Safe Path Suggestions

Diagnostic errors surface to users through `archify/bin/archify.mjs` with specific line references for actionable guidance:

| Line | Context | Behavior |
|------|---------|----------|
| L462 | Initial render validation | Validates output path before processing |
| L658 | Batch render mode | Applies checks to each output in sequence |
| L817 | Watch mode restart | Re-validates paths on file change |
| L1059 | CLI top-level error handler | Formats diagnostics with fix suggestions |

When validation fails, the CLI automatically suggests a **sandboxed location** under `repoRoot/.archify/output/` as an alternative.

## Practical Examples

### Safe default location

```bash
archify render diagram.archify

# → Resolves to <cwd>/.archify/output/diagram.html

```

### Explicit safe relative path

```bash
archify render diagram.archify -o docs/arch/flow.html

# → Validates: <cwd>/docs/arch/flow.html inside repo ✓

# → Extension: .html ✓

```

### Blocked unsafe absolute path

```bash
archify render diagram.archify -o /etc/passwd

# Error: output path is outside the repository – choose a safe output path and retry

```

### Blocked path traversal attempt

```bash
archify render diagram.archify -o ../../../tmp/exploit.html

# Error: output path is outside the repository – choose a safe output path and retry

```

## Summary

- **Resolution**: All paths are normalized to absolute form using `path.resolve(cwd, rawOutput)` at L282
- **Containment**: `pathIsInside(cwd, outputPath)` blocks directory traversal attacks at L289-L290
- **Type safety**: Meta sources require `.html` output extensions at L289
- **Deduplication**: Pending write scanning prevents collisions at L298-L312
- **Concurrency**: `outputPathGuards` map in `cli.mjs` (L13-L68) enables atomic claims
- **UX recovery**: Diagnostics in `archify.mjs` (L462, L658, L817, L1059) guide users to safe alternatives

## Frequently Asked Questions

### How does Archify prevent writing files outside the repository?

Archify uses the `pathIsInside()` utility to verify that the resolved absolute path remains within the current working directory. This check executes immediately after path normalization in `archify/renderers/shared/output-path.mjs` (L289-L290). Any path that resolves above the repository root—whether through `../` traversal or absolute paths—triggers a diagnostic with guidance to choose a safe output path.

### What happens if two inputs would generate the same output file?

Before committing any write, the renderer iterates through `otherOutputPath` to detect conflicts (L298-L312). When a collision is found, Archify throws an `output_path_collision` diagnostic naming the existing source file. This prevents accidental overwrites during batch operations or when multiple `.archify` files target similar naming conventions.

### Does Archify allow non-HTML output formats?

For **meta sources** (`.archify` description files), the renderer enforces `.html` extensions at L289. This restriction ensures rendered outputs are safe web-viewable artifacts. Direct API usage or other source types may have different constraints, but the CLI pathway requires HTML for meta-driven renders.

### How are concurrent CLI invocations protected from race conditions?

The `outputPathGuards` Map in `archify/renderers/shared/cli.mjs` (L13-L68) tracks claimed paths across asynchronous operations. Each successful claim returns a release function; duplicate claims for the same resolved path trigger `concurrent_output_conflict` diagnostics. This design provides atomicity without file-system locking.