ciphergate-cli 0.7.0__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/CHANGELOG.md +23 -1
  2. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/PKG-INFO +27 -2
  3. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/README.md +26 -1
  4. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/pyproject.toml +3 -0
  5. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/__init__.py +6 -1
  6. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/cli.py +15 -2
  7. ciphergate_cli-0.8.0/src/ciphergate/policy/templates.py +42 -0
  8. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_cli_e2e.py +30 -0
  9. ciphergate_cli-0.8.0/tests/unit/test_policy_templates.py +55 -0
  10. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/.gitignore +0 -0
  11. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/LICENSE +0 -0
  12. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/NOTICE +0 -0
  13. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/SECURITY.md +0 -0
  14. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/architecture.md +0 -0
  15. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/claude-code-tooling.md +0 -0
  16. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/detector-coverage.md +0 -0
  17. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/examples.md +0 -0
  18. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/limitations.md +0 -0
  19. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/policy-schema.md +0 -0
  20. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/security-and-privacy.md +0 -0
  21. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/threat-model.md +0 -0
  22. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.csv +0 -0
  23. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.json +0 -0
  24. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.jsonl +0 -0
  25. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.log +0 -0
  26. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.csv +0 -0
  27. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.json +0 -0
  28. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.jsonl +0 -0
  29. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.log +0 -0
  30. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/dev_local.yml +0 -0
  31. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/developer_logs.yml +0 -0
  32. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/external_ai.yml +0 -0
  33. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/financial_template.yml +0 -0
  34. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/healthcare_template.yml +0 -0
  35. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/local_debug.yml +0 -0
  36. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/multi_environment.yml +0 -0
  37. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/prd_external_ai.yml +0 -0
  38. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/stg_test.yml +0 -0
  39. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/test_dataset.yml +0 -0
  40. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/__main__.py +0 -0
  41. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/config.py +0 -0
  42. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/constants.py +0 -0
  43. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/__init__.py +0 -0
  44. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/base.py +0 -0
  45. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/custom_regex.py +0 -0
  46. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/dates.py +0 -0
  47. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/email.py +0 -0
  48. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/field_names.py +0 -0
  49. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/ip_address.py +0 -0
  50. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/network.py +0 -0
  51. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/payment_card.py +0 -0
  52. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/phone.py +0 -0
  53. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/presidio_ner.py +0 -0
  54. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/regex_safety.py +0 -0
  55. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/registry.py +0 -0
  56. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/secrets.py +0 -0
  57. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/errors.py +0 -0
  58. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/__init__.py +0 -0
  59. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/context.py +0 -0
  60. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/finding.py +0 -0
  61. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/policy.py +0 -0
  62. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/report.py +0 -0
  63. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/__init__.py +0 -0
  64. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/atomic_output.py +0 -0
  65. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/batch.py +0 -0
  66. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/inspector.py +0 -0
  67. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/paths.py +0 -0
  68. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/planner.py +0 -0
  69. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/rescan.py +0 -0
  70. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/sanitizer.py +0 -0
  71. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/stdio.py +0 -0
  72. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/streams.py +0 -0
  73. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/__init__.py +0 -0
  74. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/context.py +0 -0
  75. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/engine.py +0 -0
  76. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/loader.py +0 -0
  77. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/precedence.py +0 -0
  78. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/validator.py +0 -0
  79. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/py.typed +0 -0
  80. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/__init__.py +0 -0
  81. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/accesslog_reader.py +0 -0
  82. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/base.py +0 -0
  83. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/csv_reader.py +0 -0
  84. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/json_reader.py +0 -0
  85. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/jsonl_reader.py +0 -0
  86. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/jsonlog_reader.py +0 -0
  87. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/line_structure.py +0 -0
  88. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/logfmt_reader.py +0 -0
  89. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/stacktrace_reader.py +0 -0
  90. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/syslog_reader.py +0 -0
  91. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/text_reader.py +0 -0
  92. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/__init__.py +0 -0
  93. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/audit.py +0 -0
  94. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/safe_text.py +0 -0
  95. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/serializer.py +0 -0
  96. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/terminal.py +0 -0
  97. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/templates/starter_policy.yml +0 -0
  98. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/__init__.py +0 -0
  99. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/date_shift.py +0 -0
  100. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/generalize.py +0 -0
  101. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/keys.py +0 -0
  102. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/pseudonymize.py +0 -0
  103. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/redact.py +0 -0
  104. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/registry.py +0 -0
  105. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/replace.py +0 -0
  106. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/__init__.py +0 -0
  107. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/conftest.py +0 -0
  108. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/__init__.py +0 -0
  109. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_api_batch_streams.py +0 -0
  110. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_atomic_output.py +0 -0
  111. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_batch.py +0 -0
  112. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_domain_templates.py +0 -0
  113. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_environment_matrix.py +0 -0
  114. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_pipelines.py +0 -0
  115. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_stdio.py +0 -0
  116. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/regression/__init__.py +0 -0
  117. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/regression/test_known_cases.py +0 -0
  118. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/__init__.py +0 -0
  119. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_key_file.py +0 -0
  120. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_no_network.py +0 -0
  121. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_no_raw_value_leaks.py +0 -0
  122. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_path_safety.py +0 -0
  123. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_policy_fail_closed.py +0 -0
  124. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_review_findings.py +0 -0
  125. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/__init__.py +0 -0
  126. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_ci_supply_chain.py +0 -0
  127. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_claude_tooling.py +0 -0
  128. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_git_tooling.py +0 -0
  129. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_publish_workflow.py +0 -0
  130. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/__init__.py +0 -0
  131. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_accesslog.py +0 -0
  132. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_detectors.py +0 -0
  133. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_jsonlog.py +0 -0
  134. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_logfmt.py +0 -0
  135. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_engine.py +0 -0
  136. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_precedence.py +0 -0
  137. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_validation.py +0 -0
  138. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_presidio_ner.py +0 -0
  139. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_properties.py +0 -0
  140. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_readers.py +0 -0
  141. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_stacktrace.py +0 -0
  142. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_syslog.py +0 -0
  143. {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_transforms.py +0 -0
@@ -6,6 +6,26 @@ versioning: [PEP 440](https://peps.python.org/pep-0440/) with SemVer meaning (pr
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.8.0] - 2026-10-30
10
+
11
+ Adds the packaged policy templates to the Python API. Existing behaviour is unchanged.
12
+
13
+ ### Added
14
+
15
+ - Python API for the packaged policy templates: `load_template(name)` returns a `Policy` and
16
+ `template_names()` lists the names. An unknown name raises `UsageError`.
17
+
18
+ ## [0.7.1] - 2026-10-30
19
+
20
+ Makes the starter policy templates available after `pip install`. Existing commands and
21
+ template contents are unchanged.
22
+
23
+ ### Added
24
+
25
+ - The starter policy templates now ship in the wheel. `ciphergate init --template NAME` writes
26
+ one to a file and `ciphergate init --list-templates` lists them, so a `pip install` no longer
27
+ needs a repository clone to get them. Plain `ciphergate init` is unchanged.
28
+
9
29
  ## [0.7.0] - 2026-10-30
10
30
 
11
31
  First release published to PyPI. CLI, API and policy behaviour are unchanged, except for the
@@ -259,7 +279,9 @@ Pre-release (alpha). Licensed under Apache-2.0. Not a final release: the securit
259
279
  See `docs/limitations.md`. Deferred: optional NER/Presidio detectors, stdin/stdout streaming,
260
280
  directory processing, structured log parsers, key files, reversible token vault, lossy decoding.
261
281
 
262
- [Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.7.0...HEAD
282
+ [Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.8.0...HEAD
283
+ [0.8.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.7.1...v0.8.0
284
+ [0.7.1]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.7.0...v0.7.1
263
285
  [0.7.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.6.1...v0.7.0
264
286
  [0.6.1]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.6.0...v0.6.1
265
287
  [0.6.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.5.0...v0.6.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ciphergate-cli
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Local-first, environment- and destination-aware inspection and sanitization of logs and test datasets.
5
5
  Project-URL: Homepage, https://github.com/aashishkumar-tech/ciphergate
6
6
  Project-URL: Source, https://github.com/aashishkumar-tech/ciphergate
@@ -50,7 +50,7 @@ vendor ticket. CipherGate replaces that with a local, explainable, scriptable ga
50
50
  inspect -> plan -> sanitize --apply -> rescan
51
51
  ```
52
52
 
53
- > **Status: 0.7.0 (alpha).** No detector finds every sensitive value. A passing run means the
53
+ > **Status: 0.8.0 (alpha).** No detector finds every sensitive value. A passing run means the
54
54
  > configured processing completed under the selected policy; it is **not** proof that the output
55
55
  > contains no sensitive information, and it is not a compliance (GDPR, HIPAA, PCI DSS, ...) or
56
56
  > anonymization guarantee. Policy templates must be reviewed before use.
@@ -117,8 +117,24 @@ ciphergate --version
117
117
 
118
118
  Runtime dependencies: `typer`, `pydantic`, `PyYAML`.
119
119
 
120
+ ## Quick start after `pip install`
121
+
122
+ The policy templates ship with the package. Copy one, review it, then run it on your own file:
123
+
124
+ ```bash
125
+ ciphergate init --list-templates
126
+ ciphergate init --template developer_logs --output developer_logs.yml
127
+ ciphergate validate-policy developer_logs.yml
128
+ ciphergate inspect app.log --policy developer_logs.yml --environment dev --destination local_dev
129
+ ```
130
+
131
+ Every template is a starting point that requires review for your data and destination.
132
+
120
133
  ## Quick start (synthetic examples)
121
134
 
135
+ These commands run from a clone of this repository, because `examples/` is not in the package.
136
+ `policies/` in a clone holds the same templates that `ciphergate init --template` writes.
137
+
122
138
  ```bash
123
139
  # 1. Start from a template (refuses to overwrite without --force) and validate it.
124
140
  ciphergate init --output ciphergate-policy.yml
@@ -478,6 +494,15 @@ report = sanitize_file("app.log", "app.clean.log", policy,
478
494
  print(report.status, report.exit_code)
479
495
  ```
480
496
 
497
+ After `pip install`, there is no `policies/` folder. Load a packaged template by name instead;
498
+ `template_names()` lists them. Templates require review for your data and destination.
499
+
500
+ ```python
501
+ from ciphergate import load_template, template_names
502
+ print(template_names())
503
+ policy = load_template("developer_logs")
504
+ ```
505
+
481
506
  Streams and directories use the same pipeline and the same safety rules as the CLI:
482
507
 
483
508
  ```python
@@ -11,7 +11,7 @@ vendor ticket. CipherGate replaces that with a local, explainable, scriptable ga
11
11
  inspect -> plan -> sanitize --apply -> rescan
12
12
  ```
13
13
 
14
- > **Status: 0.7.0 (alpha).** No detector finds every sensitive value. A passing run means the
14
+ > **Status: 0.8.0 (alpha).** No detector finds every sensitive value. A passing run means the
15
15
  > configured processing completed under the selected policy; it is **not** proof that the output
16
16
  > contains no sensitive information, and it is not a compliance (GDPR, HIPAA, PCI DSS, ...) or
17
17
  > anonymization guarantee. Policy templates must be reviewed before use.
@@ -78,8 +78,24 @@ ciphergate --version
78
78
 
79
79
  Runtime dependencies: `typer`, `pydantic`, `PyYAML`.
80
80
 
81
+ ## Quick start after `pip install`
82
+
83
+ The policy templates ship with the package. Copy one, review it, then run it on your own file:
84
+
85
+ ```bash
86
+ ciphergate init --list-templates
87
+ ciphergate init --template developer_logs --output developer_logs.yml
88
+ ciphergate validate-policy developer_logs.yml
89
+ ciphergate inspect app.log --policy developer_logs.yml --environment dev --destination local_dev
90
+ ```
91
+
92
+ Every template is a starting point that requires review for your data and destination.
93
+
81
94
  ## Quick start (synthetic examples)
82
95
 
96
+ These commands run from a clone of this repository, because `examples/` is not in the package.
97
+ `policies/` in a clone holds the same templates that `ciphergate init --template` writes.
98
+
83
99
  ```bash
84
100
  # 1. Start from a template (refuses to overwrite without --force) and validate it.
85
101
  ciphergate init --output ciphergate-policy.yml
@@ -439,6 +455,15 @@ report = sanitize_file("app.log", "app.clean.log", policy,
439
455
  print(report.status, report.exit_code)
440
456
  ```
441
457
 
458
+ After `pip install`, there is no `policies/` folder. Load a packaged template by name instead;
459
+ `template_names()` lists them. Templates require review for your data and destination.
460
+
461
+ ```python
462
+ from ciphergate import load_template, template_names
463
+ print(template_names())
464
+ policy = load_template("developer_logs")
465
+ ```
466
+
442
467
  Streams and directories use the same pipeline and the same safety rules as the CLI:
443
468
 
444
469
  ```python
@@ -62,6 +62,9 @@ path = "src/ciphergate/__init__.py"
62
62
 
63
63
  [tool.hatch.build.targets.wheel]
64
64
  packages = ["src/ciphergate"]
65
+ # policies/ stays the one source of the starter templates; `ciphergate init --template` reads them.
66
+ [tool.hatch.build.targets.wheel.force-include]
67
+ "policies" = "ciphergate/policies"
65
68
 
66
69
  [tool.hatch.build.targets.sdist]
67
70
  include = [
@@ -3,6 +3,8 @@
3
3
  Supported public API (everything else is internal and may change without notice)::
4
4
 
5
5
  load_policy(path) -> Policy
6
+ load_template(name) -> Policy # a packaged template; review it before relying on it
7
+ template_names() -> list[str]
6
8
  validate_policy(path_or_policy) -> ValidationResult
7
9
  inspect_file(path, policy, context=None) -> RunReport
8
10
  plan_file(path, policy, context=None) -> RunReport
@@ -22,7 +24,7 @@ Supported public API (everything else is internal and may change without notice)
22
24
  rescan_directory(root, policy, context=None, *, recursive=False, strict=None) -> BatchReport
23
25
  """
24
26
 
25
- __version__ = "0.7.0"
27
+ __version__ = "0.8.0"
26
28
 
27
29
  from ciphergate.config import RuntimeSettings
28
30
  from ciphergate.constants import (
@@ -55,6 +57,7 @@ from ciphergate.pipeline.streams import (
55
57
  rescan_stream,
56
58
  sanitize_stream,
57
59
  )
60
+ from ciphergate.policy.templates import load_template, template_names
58
61
  from ciphergate.policy.validator import (
59
62
  Policy,
60
63
  ValidationResult,
@@ -82,6 +85,7 @@ __all__ = [
82
85
  "inspect_file",
83
86
  "inspect_stream",
84
87
  "load_policy",
88
+ "load_template",
85
89
  "plan_directory",
86
90
  "plan_file",
87
91
  "plan_stream",
@@ -91,5 +95,6 @@ __all__ = [
91
95
  "sanitize_directory",
92
96
  "sanitize_file",
93
97
  "sanitize_stream",
98
+ "template_names",
94
99
  "validate_policy",
95
100
  ]
@@ -74,6 +74,7 @@ from ciphergate.pipeline.stdio import (
74
74
  )
75
75
  from ciphergate.policy.context import evaluate_gates, resolve_context
76
76
  from ciphergate.policy.engine import PolicyEngine
77
+ from ciphergate.policy.templates import read_template, template_names
77
78
  from ciphergate.policy.validator import load_policy, validate_policy
78
79
  from ciphergate.reporting.safe_text import escape_control
79
80
  from ciphergate.reporting.serializer import (
@@ -351,6 +352,13 @@ def init_command(
351
352
  output: Annotated[
352
353
  Path, typer.Option("--output", "-o", help="Where to write the policy.")
353
354
  ] = Path("ciphergate-policy.yml"),
355
+ template: Annotated[
356
+ str | None,
357
+ typer.Option("--template", "-t", help="Named template (see --list-templates)."),
358
+ ] = None,
359
+ list_templates: Annotated[
360
+ bool, typer.Option("--list-templates", help="List named templates and exit.")
361
+ ] = False,
354
362
  force: ForceOpt = False,
355
363
  quiet: QuietOpt = False,
356
364
  debug: DebugOpt = False,
@@ -358,12 +366,17 @@ def init_command(
358
366
  """Write a starter policy template (it MUST be reviewed before real use)."""
359
367
 
360
368
  def body() -> int:
361
- validate_output_path(output, protected=[], force=force, label="policy")
369
+ if list_templates:
370
+ sys.stdout.write("".join(f"{name}\n" for name in template_names()))
371
+ return 0
362
372
  text = (
363
- resources.files("ciphergate")
373
+ read_template(template)
374
+ if template is not None
375
+ else resources.files("ciphergate")
364
376
  .joinpath("templates/starter_policy.yml")
365
377
  .read_text(encoding="utf-8")
366
378
  )
379
+ validate_output_path(output, protected=[], force=force, label="policy")
367
380
  with AtomicOutput(output, force=force) as out:
368
381
  out.stream.write(text)
369
382
  out.publish()
@@ -0,0 +1,42 @@
1
+ """Starter policy templates shipped with the package. Every template requires review before use."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import resources
6
+ from importlib.resources.abc import Traversable
7
+ from pathlib import Path
8
+
9
+ from ciphergate.errors import UsageError
10
+ from ciphergate.policy.validator import Policy, load_policy_text
11
+
12
+
13
+ def _templates_dir() -> Traversable:
14
+ packaged = resources.files("ciphergate").joinpath("policies")
15
+ if packaged.is_dir():
16
+ return packaged
17
+ # Editable install: the build copies policies/ into wheels only, so use the source checkout.
18
+ return Path(__file__).resolve().parents[3] / "policies"
19
+
20
+
21
+ def template_names() -> list[str]:
22
+ """Names accepted by `read_template`, sorted."""
23
+ return sorted(
24
+ entry.name.removesuffix(".yml")
25
+ for entry in _templates_dir().iterdir()
26
+ if entry.name.endswith(".yml")
27
+ )
28
+
29
+
30
+ def read_template(name: str) -> str:
31
+ """Return a template's text. `name` is only compared to known names, never used as a path."""
32
+ if name not in template_names():
33
+ raise UsageError(
34
+ "unknown policy template; run: ciphergate init --list-templates",
35
+ code="unknown_policy_template",
36
+ )
37
+ return _templates_dir().joinpath(f"{name}.yml").read_text(encoding="utf-8")
38
+
39
+
40
+ def load_template(name: str) -> Policy:
41
+ """Load a named template as a `Policy`. It is a starting point that requires review."""
42
+ return load_policy_text(read_template(name), source_name=f"{name}.yml")
@@ -40,6 +40,36 @@ def test_init_refuses_overwrite(tmp_path: Path) -> None:
40
40
  assert invoke("init", "--output", target, "--force").exit_code == 0
41
41
 
42
42
 
43
+ def test_init_lists_every_policy_template() -> None:
44
+ r = invoke("init", "--list-templates")
45
+ assert r.exit_code == 0
46
+ assert r.stdout.split() == sorted(p.stem for p in POLICIES.glob("*.yml"))
47
+
48
+
49
+ @pytest.mark.parametrize("policy", sorted(POLICIES.glob("*.yml")), ids=lambda p: p.stem)
50
+ def test_init_template_writes_a_valid_copy(tmp_path: Path, policy: Path) -> None:
51
+ target = tmp_path / "p.yml"
52
+ r = invoke("init", "--template", policy.stem, "--output", target)
53
+ assert r.exit_code == 0 and "template" in r.stdout.lower()
54
+ assert target.read_text(encoding="utf-8") == policy.read_text(encoding="utf-8")
55
+ assert invoke("validate-policy", target).exit_code == 0
56
+
57
+
58
+ def test_init_template_refuses_overwrite(tmp_path: Path) -> None:
59
+ target = tmp_path / "p.yml"
60
+ target.write_text("keep", encoding="utf-8")
61
+ assert invoke("init", "--template", "dev_local", "--output", target).exit_code == 5
62
+ assert target.read_text(encoding="utf-8") == "keep"
63
+
64
+
65
+ @pytest.mark.parametrize("name", ["nope", "../pyproject", "dev_local.yml", ""])
66
+ def test_init_unknown_template_fails_and_writes_nothing(tmp_path: Path, name: str) -> None:
67
+ target = tmp_path / "p.yml"
68
+ r = invoke("init", "--template", name, "--output", target)
69
+ assert r.exit_code == 2
70
+ assert "--list-templates" in r.output and not target.exists()
71
+
72
+
43
73
  def test_validate_policy_exit_codes(tmp_path: Path) -> None:
44
74
  bad = tmp_path / "bad.yml"
45
75
  bad.write_text(
@@ -0,0 +1,55 @@
1
+ """Packaged policy templates through the public Python API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ import ciphergate
10
+ from ciphergate import (
11
+ ContextInput,
12
+ RunStatus,
13
+ load_policy,
14
+ load_template,
15
+ sanitize_file,
16
+ template_names,
17
+ )
18
+ from ciphergate.errors import CipherGateError, UsageError
19
+ from tests.conftest import EXAMPLES, POLICIES
20
+
21
+
22
+ def test_public_exports() -> None:
23
+ for name in ("load_template", "template_names"):
24
+ assert name in ciphergate.__all__ and hasattr(ciphergate, name)
25
+
26
+
27
+ def test_names_match_the_policy_files() -> None:
28
+ assert template_names() == sorted(p.stem for p in POLICIES.glob("*.yml"))
29
+
30
+
31
+ @pytest.mark.parametrize("name", template_names())
32
+ def test_load_template_matches_the_policy_file(name: str) -> None:
33
+ policy = load_template(name)
34
+ assert policy.name == load_policy(POLICIES / f"{name}.yml").name
35
+ assert policy.source_name == f"{name}.yml"
36
+
37
+
38
+ @pytest.mark.parametrize("name", ["nope", "../pyproject", "dev_local.yml", ""])
39
+ def test_unknown_template_is_rejected(name: str) -> None:
40
+ with pytest.raises(UsageError) as err:
41
+ load_template(name)
42
+ assert err.value.code == "unknown_policy_template"
43
+ assert isinstance(err.value, CipherGateError)
44
+
45
+
46
+ def test_loaded_template_still_blocks_and_publishes_nothing(tmp_path: Path) -> None:
47
+ output = tmp_path / "out.log"
48
+ report = sanitize_file(
49
+ EXAMPLES / "sample.log",
50
+ output,
51
+ load_template("prd_external_ai"),
52
+ ContextInput(),
53
+ )
54
+ assert report.status == RunStatus.BLOCKED and report.exit_code == 1
55
+ assert not output.exists()
File without changes
File without changes