# `orchestrator.registry.api.workspace`
<!-- generated by `orchestrator understand`; the PKG is source of truth, edits here are advisory -->

[← Episteme](../README.md) · [Architecture](../architecture.md)

**Source:** [`src/orchestrator/registry/api/workspace.py`](../../src/orchestrator/registry/api/workspace.py)

3 types · 13 functions · python

## Changing this safely

**Tested by** (2): `tests.registry.test_repo_sources`, `tests.registry.test_workspace`

**Most depended-upon here** — a change to these reaches the most code (call graph, ≤4 hops):

- [`resolve_repo_source`](../../src/orchestrator/registry/api/workspace.py#L166) — reaches **36** symbols
- [`RepoSourceError`](../../src/orchestrator/registry/api/workspace.py#L42) — reaches **35** symbols
- [`materialize_repo_source`](../../src/orchestrator/registry/api/workspace.py#L216) — reaches **33** symbols
- [`_is_git_url`](../../src/orchestrator/registry/api/workspace.py#L93) — reaches **32** symbols
- [`_resolve_local`](../../src/orchestrator/registry/api/workspace.py#L155) — reaches **32** symbols
- [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132) — reaches **32** symbols
- [`RepoPathError`](../../src/orchestrator/registry/api/workspace.py#L38) — reaches **29** symbols
- [`RepoSource`](../../src/orchestrator/registry/api/workspace.py#L83) — reaches **29** symbols

## Types

### `RepoPathError`

[`src/orchestrator/registry/api/workspace.py:38`](../../src/orchestrator/registry/api/workspace.py#L38)

- **Extends** (1): `ValueError`
- **Called by** (2): [`_resolve_local`](../../src/orchestrator/registry/api/workspace.py#L155), [`resolve_repo_path`](../../src/orchestrator/registry/api/workspace.py#L53)

### `RepoSource`

[`src/orchestrator/registry/api/workspace.py:83`](../../src/orchestrator/registry/api/workspace.py#L83)

- **Called by** (2 production · 3 test): [`_resolve_local`](../../src/orchestrator/registry/api/workspace.py#L155), [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132), [`test_clone_failure_is_sanitized_error`](../../tests/registry/test_repo_sources.py#L125), [`test_materialize_git_clones_then_cleans_up`](../../tests/registry/test_repo_sources.py#L108), [`test_materialize_local_yields_path_no_clone`](../../tests/registry/test_repo_sources.py#L119)
- **Fields**: `display`, `kind`, `path`, `url`

### `RepoSourceError`

[`src/orchestrator/registry/api/workspace.py:42`](../../src/orchestrator/registry/api/workspace.py#L42)

- **Extends** (1): `ValueError`
- **Called by** (2): [`_git_clone`](../../src/orchestrator/registry/api/workspace.py#L188), [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132)

## Functions

### `_allowed_hosts`

[`src/orchestrator/registry/api/workspace.py:98`](../../src/orchestrator/registry/api/workspace.py#L98)

- **Called by** (1): [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132)

### `_git_clone`

[`src/orchestrator/registry/api/workspace.py:188`](../../src/orchestrator/registry/api/workspace.py#L188)

- **Called by** (1): [`materialize_repo_source`](../../src/orchestrator/registry/api/workspace.py#L216)
- **Calls** (6): [`RepoSourceError`](../../src/orchestrator/registry/api/workspace.py#L42), [`_sanitize`](../../src/orchestrator/registry/api/workspace.py#L179), [`authenticate_repo_url`](../../src/orchestrator/sdlc/gitauth.py#L110), `run`, `run`, `suppress`

### `_host_allowed`

[`src/orchestrator/registry/api/workspace.py:125`](../../src/orchestrator/registry/api/workspace.py#L125)

- **Called by** (1): [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132)

### `_ip_is_internal`

[`src/orchestrator/registry/api/workspace.py:103`](../../src/orchestrator/registry/api/workspace.py#L103)

- **Called by** (1): [`_is_internal_host`](../../src/orchestrator/registry/api/workspace.py#L107)

### `_is_git_url`

[`src/orchestrator/registry/api/workspace.py:93`](../../src/orchestrator/registry/api/workspace.py#L93)

- **Called by** (1): [`resolve_repo_source`](../../src/orchestrator/registry/api/workspace.py#L166)

### `_is_internal_host`

[`src/orchestrator/registry/api/workspace.py:107`](../../src/orchestrator/registry/api/workspace.py#L107)

- **Called by** (1): [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132)
- **Calls** (4): [`_ip_is_internal`](../../src/orchestrator/registry/api/workspace.py#L103), `inet_aton`, `ip_address`, `suppress`

### `_resolve_local`

[`src/orchestrator/registry/api/workspace.py:155`](../../src/orchestrator/registry/api/workspace.py#L155)

- **Called by** (1): [`resolve_repo_source`](../../src/orchestrator/registry/api/workspace.py#L166)
- **Calls** (4): [`RepoPathError`](../../src/orchestrator/registry/api/workspace.py#L38), [`RepoSource`](../../src/orchestrator/registry/api/workspace.py#L83), `pathlib.Path`, [`resolve_repo_path`](../../src/orchestrator/registry/api/workspace.py#L53)

### `_sanitize`

[`src/orchestrator/registry/api/workspace.py:179`](../../src/orchestrator/registry/api/workspace.py#L179)

- **Called by** (1 production · 1 test): [`_git_clone`](../../src/orchestrator/registry/api/workspace.py#L188), [`test_sanitize_scrubs_token`](../../tests/registry/test_repo_sources.py#L131)
- **Calls** (1): `sub`

### `_validate_git_url`

[`src/orchestrator/registry/api/workspace.py:132`](../../src/orchestrator/registry/api/workspace.py#L132)

- **Called by** (1 production · 3 test): [`resolve_repo_source`](../../src/orchestrator/registry/api/workspace.py#L166), [`test_internal_hosts_blocked_even_with_wildcard_allowlist`](../../tests/registry/test_workspace.py#L44), [`test_non_git_scheme_rejected`](../../tests/registry/test_workspace.py#L54), [`test_real_host_still_allowed_under_wildcard`](../../tests/registry/test_workspace.py#L49)
- **Calls** (6): [`RepoSource`](../../src/orchestrator/registry/api/workspace.py#L83), [`RepoSourceError`](../../src/orchestrator/registry/api/workspace.py#L42), [`_allowed_hosts`](../../src/orchestrator/registry/api/workspace.py#L98), [`_host_allowed`](../../src/orchestrator/registry/api/workspace.py#L125), [`_is_internal_host`](../../src/orchestrator/registry/api/workspace.py#L107), `urllib.parse.urlsplit`

### `materialize_repo_source`

[`src/orchestrator/registry/api/workspace.py:216`](../../src/orchestrator/registry/api/workspace.py#L216)

- **Called by** (7 production · 3 test): [`_open_repo`](../../src/orchestrator/plugin/server.py#L183), [`_repo_arg`](../../src/orchestrator/cli.py#L105), [`_run`](../../src/orchestrator/registry/api/capabilities.py#L59), [`adapter`](../../src/orchestrator/registry/api/capabilities.py#L325), [`adapter`](../../src/orchestrator/registry/api/capabilities.py#L295), [`adapter`](../../src/orchestrator/registry/api/capabilities.py#L261), [`adapter`](../../src/orchestrator/registry/api/capabilities.py#L218), [`test_clone_failure_is_sanitized_error`](../../tests/registry/test_repo_sources.py#L125), [`test_materialize_git_clones_then_cleans_up`](../../tests/registry/test_repo_sources.py#L108), [`test_materialize_local_yields_path_no_clone`](../../tests/registry/test_repo_sources.py#L119)
- **Calls** (4): [`_git_clone`](../../src/orchestrator/registry/api/workspace.py#L188), `mkdtemp`, `pathlib.Path`, `rmtree`
- **Documented in**: `docs/specs/comprehension-skill-spec.md#phases`, `docs/specs/comprehension-skill-spec.md#what-already-exists-reuse-dont-rebuild`

### `resolve_repo_path`

[`src/orchestrator/registry/api/workspace.py:53`](../../src/orchestrator/registry/api/workspace.py#L53)

- **Called by** (1 production · 6 test): [`_resolve_local`](../../src/orchestrator/registry/api/workspace.py#L155), [`test_accepts_absolute_path_inside_root`](../../tests/registry/test_workspace.py#L86), [`test_rejects_absolute_path_outside_root`](../../tests/registry/test_workspace.py#L77), [`test_rejects_file_not_directory`](../../tests/registry/test_workspace.py#L97), [`test_rejects_missing_path`](../../tests/registry/test_workspace.py#L92), [`test_rejects_parent_traversal`](../../tests/registry/test_workspace.py#L69), [`test_resolves_root_and_subdir`](../../tests/registry/test_workspace.py#L60)
- **Calls** (3): [`RepoPathError`](../../src/orchestrator/registry/api/workspace.py#L38), `pathlib.Path`, [`workspace_root`](../../src/orchestrator/registry/api/workspace.py#L46)

### `resolve_repo_source`

[`src/orchestrator/registry/api/workspace.py:166`](../../src/orchestrator/registry/api/workspace.py#L166)

- **Called by** (3 production · 9 test): [`_open_repo`](../../src/orchestrator/plugin/server.py#L183), [`_repo_arg`](../../src/orchestrator/cli.py#L105), [`_source`](../../src/orchestrator/registry/api/capabilities.py#L46), [`test_any_local_flag_allows_absolute_outside_root`](../../tests/registry/test_repo_sources.py#L81), [`test_disallowed_host_rejected`](../../tests/registry/test_repo_sources.py#L48), [`test_enterprise_subdomain_allowed_via_config`](../../tests/registry/test_repo_sources.py#L58), [`test_file_scheme_rejected`](../../tests/registry/test_repo_sources.py#L63), [`test_github_url_allowed_by_default`](../../tests/registry/test_repo_sources.py#L38), [`test_internal_hosts_rejected_even_with_wildcard`](../../tests/registry/test_repo_sources.py#L68), [`test_local_path_classified_and_scoped`](../../tests/registry/test_repo_sources.py#L32), [`test_scp_style_ssh_url_allowed`](../../tests/registry/test_repo_sources.py#L43), [`test_wildcard_allows_any_public_host`](../../tests/registry/test_repo_sources.py#L53)
- **Calls** (3): [`_is_git_url`](../../src/orchestrator/registry/api/workspace.py#L93), [`_resolve_local`](../../src/orchestrator/registry/api/workspace.py#L155), [`_validate_git_url`](../../src/orchestrator/registry/api/workspace.py#L132)
- **Documented in**: `docs/specs/comprehension-skill-spec.md#design-decisions`, `docs/specs/comprehension-skill-spec.md#phases`, `docs/specs/comprehension-skill-spec.md#what-already-exists-reuse-dont-rebuild`

### `workspace_root`

[`src/orchestrator/registry/api/workspace.py:46`](../../src/orchestrator/registry/api/workspace.py#L46)

- **Called by** (2 production · 1 test): [`_default_dir`](../../src/orchestrator/registry/api/fs.py#L43), [`resolve_repo_path`](../../src/orchestrator/registry/api/workspace.py#L53), [`test_unset_root_defaults_to_cwd`](../../tests/registry/test_workspace.py#L103)
- **Calls** (2): `cwd`, `pathlib.Path`

## Imports

`__future__.annotations`, `asyncio`, `collections.abc.Callable`, `collections.abc.Iterator`, `contextlib`, `dataclasses.dataclass`, `ipaddress`, [`orchestrator.registry.api.config`](../../src/orchestrator/registry/api/config.py#L1), [`orchestrator.sdlc.gitauth`](../../src/orchestrator/sdlc/gitauth.py#L1), `os`, `pathlib.Path`, `re`, `shutil`, `socket`, `subprocess`, `tempfile`, `urllib.parse.urlsplit`

## Imported by

[`orchestrator.cli`](orchestrator.cli.md), [`orchestrator.plugin.server`](orchestrator.plugin.server.md), [`orchestrator.registry.api.capabilities`](orchestrator.registry.api.capabilities.md), [`orchestrator.registry.api.fs`](../../src/orchestrator/registry/api/fs.py#L1), [`tests.registry.test_repo_sources`](../../tests/registry/test_repo_sources.py#L1), [`tests.registry.test_workspace`](../../tests/registry/test_workspace.py#L1)
