verityledger 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tracewell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: verityledger
3
+ Version: 0.1.0
4
+ Summary: Tamper-evident decision and tool-call logging for AI agents.
5
+ Author: VerityLedger
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/verityledger/verityledger
8
+ Project-URL: Issues, https://github.com/verityledger/verityledger/issues
9
+ Keywords: ai,agents,audit,logging,llm,observability
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
20
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
21
+ Requires-Dist: ruff>=0.6; extra == "dev"
22
+ Requires-Dist: mypy>=1.10; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # VerityLedger
26
+
27
+ **Know exactly what your AI agent did, and prove nothing was changed after the fact.**
28
+
29
+ VerityLedger is a small Python library that wraps your agent's tool calls and
30
+ decisions in a tamper-evident, hash-chained log. When something goes wrong —
31
+ a bad refund, a broken deploy, a strange customer reply — you can pull up the
32
+ exact sequence of tool calls, model inputs/outputs, and reasoning that led
33
+ there, and prove the record hasn't been altered.
34
+
35
+ No database. No external service. No blockchain. Just an append-only file
36
+ and SHA-256.
37
+
38
+ ```python
39
+ from verityledger import Tracer
40
+
41
+ tracer = Tracer() # writes to ./verityledger_log.jsonl
42
+
43
+ with tracer.session(agent="support-bot", user="user_123") as session:
44
+
45
+ @session.trace_tool
46
+ def issue_refund(order_id: str, amount: float) -> str:
47
+ return f"refunded {amount} for {order_id}"
48
+
49
+ issue_refund("ORD-4471", 42.00)
50
+
51
+ session.log_decision(
52
+ "approved refund",
53
+ reasoning="customer reported damaged item, photo provided, within policy",
54
+ )
55
+ ```
56
+
57
+ Every call to `issue_refund`, every `log_decision`, and any model calls you
58
+ log are written as chained entries — each one includes a hash of the
59
+ previous entry. If anyone edits a past entry, the chain breaks at exactly
60
+ that point.
61
+
62
+ ## Why this exists
63
+
64
+ Agents are making real decisions — refunds, emails, code pushes, customer
65
+ replies — and most teams have no record of *why* beyond scattered print
66
+ statements and provider dashboards. When a regulator, a customer, or your
67
+ own team asks "why did the bot do that?", you want an answer that's both
68
+ complete and verifiable.
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ pip install verityledger
74
+ ```
75
+
76
+ ## Verify the chain
77
+
78
+ ```python
79
+ valid, break_index = tracer.verify(session.id)
80
+ # valid == True, break_index == None (until someone tampers with the log)
81
+
82
+ # Or raise on problems:
83
+ tracer.assert_valid(session.id)
84
+ # raises ChainIntegrityError or SessionNotFoundError
85
+ ```
86
+
87
+ ## Export an audit report
88
+
89
+ ```python
90
+ tracer.export_report(session.id, "incident_report.json")
91
+ ```
92
+
93
+ Produces a single JSON file with every entry for that session, plus the
94
+ verification result — ready to attach to an incident review or compliance
95
+ request.
96
+
97
+ ## CLI
98
+
99
+ Installing the package also installs a `verityledger` command:
100
+
101
+ ```bash
102
+ verityledger sessions # list session ids in the log
103
+ verityledger show <session_id> # print all entries for a session
104
+ verityledger verify <session_id> # check the hash chain for tampering
105
+ verityledger export <session_id> report.json # write a JSON audit report
106
+ ```
107
+
108
+ All commands accept `--log PATH` to point at a specific log file
109
+ (default: `./verityledger_log.jsonl`).
110
+
111
+ ## Architecture
112
+
113
+ The library is layered so each piece can be tested and replaced independently:
114
+
115
+ - **`chain.py`** — the cryptographic primitive. Defines `Entry`, hashing,
116
+ and `verify_chain`. No I/O, no dependencies.
117
+ - **`storage/`** — the `Store` interface plus `LocalStore` (append-only
118
+ JSONL). A hosted backend (SQLite/Postgres/API) implements the same
119
+ interface and drops in without touching anything above it.
120
+ - **`core.py`** — the public API: `Tracer` and `Session`.
121
+ - **`cli/`** — the `verityledger` terminal command. Built entirely on top of
122
+ the public API above; no logic of its own beyond argument parsing
123
+ and output formatting.
124
+ - **`exceptions.py`** — shared error types (`StorageError`,
125
+ `ChainIntegrityError`, `SessionNotFoundError`).
126
+
127
+ ## Development
128
+
129
+ ```bash
130
+ pip install -e ".[dev]"
131
+ ruff check . # lint
132
+ mypy src/verityledger # strict type check
133
+ pytest # tests + coverage
134
+ ```
135
+
136
+ ## Status
137
+
138
+ Early release. The local file-based logger (above) is free and open source
139
+ (MIT) — your data never leaves your machine. A hosted dashboard for
140
+ searching across sessions, team access, and longer retention is in
141
+ development.
142
+
143
+ ## Roadmap
144
+
145
+ - [x] Hash-chained local logging (Python)
146
+ - [x] Tool-call decorator, decision logging, model-call logging
147
+ - [x] Tamper detection / chain verification
148
+ - [x] Audit report export
149
+ - [x] Full test suite, type checking, CI
150
+ - [x] CLI (`verityledger sessions/show/verify/export`)
151
+ - [ ] JavaScript/TypeScript SDK
152
+ - [ ] Hosted dashboard (search, team accounts, retention policies)
153
+ - [ ] LangChain / OpenAI / Anthropic tool-use integrations
154
+ - [ ] Remote ingestion endpoint (send logs to VerityLedger Cloud)
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,134 @@
1
+ # VerityLedger
2
+
3
+ **Know exactly what your AI agent did, and prove nothing was changed after the fact.**
4
+
5
+ VerityLedger is a small Python library that wraps your agent's tool calls and
6
+ decisions in a tamper-evident, hash-chained log. When something goes wrong —
7
+ a bad refund, a broken deploy, a strange customer reply — you can pull up the
8
+ exact sequence of tool calls, model inputs/outputs, and reasoning that led
9
+ there, and prove the record hasn't been altered.
10
+
11
+ No database. No external service. No blockchain. Just an append-only file
12
+ and SHA-256.
13
+
14
+ ```python
15
+ from verityledger import Tracer
16
+
17
+ tracer = Tracer() # writes to ./verityledger_log.jsonl
18
+
19
+ with tracer.session(agent="support-bot", user="user_123") as session:
20
+
21
+ @session.trace_tool
22
+ def issue_refund(order_id: str, amount: float) -> str:
23
+ return f"refunded {amount} for {order_id}"
24
+
25
+ issue_refund("ORD-4471", 42.00)
26
+
27
+ session.log_decision(
28
+ "approved refund",
29
+ reasoning="customer reported damaged item, photo provided, within policy",
30
+ )
31
+ ```
32
+
33
+ Every call to `issue_refund`, every `log_decision`, and any model calls you
34
+ log are written as chained entries — each one includes a hash of the
35
+ previous entry. If anyone edits a past entry, the chain breaks at exactly
36
+ that point.
37
+
38
+ ## Why this exists
39
+
40
+ Agents are making real decisions — refunds, emails, code pushes, customer
41
+ replies — and most teams have no record of *why* beyond scattered print
42
+ statements and provider dashboards. When a regulator, a customer, or your
43
+ own team asks "why did the bot do that?", you want an answer that's both
44
+ complete and verifiable.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install verityledger
50
+ ```
51
+
52
+ ## Verify the chain
53
+
54
+ ```python
55
+ valid, break_index = tracer.verify(session.id)
56
+ # valid == True, break_index == None (until someone tampers with the log)
57
+
58
+ # Or raise on problems:
59
+ tracer.assert_valid(session.id)
60
+ # raises ChainIntegrityError or SessionNotFoundError
61
+ ```
62
+
63
+ ## Export an audit report
64
+
65
+ ```python
66
+ tracer.export_report(session.id, "incident_report.json")
67
+ ```
68
+
69
+ Produces a single JSON file with every entry for that session, plus the
70
+ verification result — ready to attach to an incident review or compliance
71
+ request.
72
+
73
+ ## CLI
74
+
75
+ Installing the package also installs a `verityledger` command:
76
+
77
+ ```bash
78
+ verityledger sessions # list session ids in the log
79
+ verityledger show <session_id> # print all entries for a session
80
+ verityledger verify <session_id> # check the hash chain for tampering
81
+ verityledger export <session_id> report.json # write a JSON audit report
82
+ ```
83
+
84
+ All commands accept `--log PATH` to point at a specific log file
85
+ (default: `./verityledger_log.jsonl`).
86
+
87
+ ## Architecture
88
+
89
+ The library is layered so each piece can be tested and replaced independently:
90
+
91
+ - **`chain.py`** — the cryptographic primitive. Defines `Entry`, hashing,
92
+ and `verify_chain`. No I/O, no dependencies.
93
+ - **`storage/`** — the `Store` interface plus `LocalStore` (append-only
94
+ JSONL). A hosted backend (SQLite/Postgres/API) implements the same
95
+ interface and drops in without touching anything above it.
96
+ - **`core.py`** — the public API: `Tracer` and `Session`.
97
+ - **`cli/`** — the `verityledger` terminal command. Built entirely on top of
98
+ the public API above; no logic of its own beyond argument parsing
99
+ and output formatting.
100
+ - **`exceptions.py`** — shared error types (`StorageError`,
101
+ `ChainIntegrityError`, `SessionNotFoundError`).
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ pip install -e ".[dev]"
107
+ ruff check . # lint
108
+ mypy src/verityledger # strict type check
109
+ pytest # tests + coverage
110
+ ```
111
+
112
+ ## Status
113
+
114
+ Early release. The local file-based logger (above) is free and open source
115
+ (MIT) — your data never leaves your machine. A hosted dashboard for
116
+ searching across sessions, team access, and longer retention is in
117
+ development.
118
+
119
+ ## Roadmap
120
+
121
+ - [x] Hash-chained local logging (Python)
122
+ - [x] Tool-call decorator, decision logging, model-call logging
123
+ - [x] Tamper detection / chain verification
124
+ - [x] Audit report export
125
+ - [x] Full test suite, type checking, CI
126
+ - [x] CLI (`verityledger sessions/show/verify/export`)
127
+ - [ ] JavaScript/TypeScript SDK
128
+ - [ ] Hosted dashboard (search, team accounts, retention policies)
129
+ - [ ] LangChain / OpenAI / Anthropic tool-use integrations
130
+ - [ ] Remote ingestion endpoint (send logs to VerityLedger Cloud)
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "verityledger"
7
+ version = "0.1.0"
8
+ description = "Tamper-evident decision and tool-call logging for AI agents."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "VerityLedger" }]
13
+ keywords = ["ai", "agents", "audit", "logging", "llm", "observability"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Software Development :: Libraries",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = []
22
+
23
+ [project.optional-dependencies]
24
+ dev = [
25
+ "pytest>=8.0",
26
+ "pytest-cov>=5.0",
27
+ "ruff>=0.6",
28
+ "mypy>=1.10",
29
+ ]
30
+
31
+ [project.scripts]
32
+ verityledger = "verityledger.cli:main"
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/verityledger/verityledger"
36
+ Issues = "https://github.com/verityledger/verityledger/issues"
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
40
+
41
+ [tool.setuptools.package-data]
42
+ verityledger = ["py.typed"]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
46
+ addopts = "--cov=verityledger --cov-report=term-missing"
47
+
48
+ [tool.ruff]
49
+ line-length = 100
50
+ src = ["src"]
51
+
52
+ [tool.ruff.lint]
53
+ select = ["E", "F", "I", "UP", "B"]
54
+
55
+ [tool.mypy]
56
+ python_version = "3.10"
57
+ strict = true
58
+ mypy_path = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ from .chain import Entry, verify_chain
2
+ from .core import Session, Tracer
3
+ from .exceptions import (
4
+ ChainIntegrityError,
5
+ SessionNotFoundError,
6
+ StorageError,
7
+ VerityLedgerError,
8
+ )
9
+ from .storage import LocalStore, Store
10
+
11
+ __all__ = [
12
+ "Tracer",
13
+ "Session",
14
+ "Entry",
15
+ "verify_chain",
16
+ "Store",
17
+ "LocalStore",
18
+ "VerityLedgerError",
19
+ "StorageError",
20
+ "ChainIntegrityError",
21
+ "SessionNotFoundError",
22
+ ]
23
+
24
+ __version__ = "0.1.0"
@@ -0,0 +1,107 @@
1
+ """
2
+ Hash-chained, tamper-evident log entries.
3
+
4
+ Each entry includes a hash of the previous entry, forming a chain.
5
+ Any modification to a past entry breaks the chain for everything after it,
6
+ making tampering detectable without needing a blockchain or external service.
7
+
8
+ This module has zero dependencies on storage or the public API — it is the
9
+ single source of truth for what an "entry" is and how the chain is verified.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import hashlib
15
+ import json
16
+ import time
17
+ import uuid
18
+ from dataclasses import asdict, dataclass, field
19
+ from typing import Any
20
+
21
+ GENESIS_HASH = "0" * 64
22
+
23
+
24
+ def _canonical_json(data: dict[str, Any]) -> str:
25
+ """Serialize a dict deterministically so hashes are reproducible."""
26
+ return json.dumps(data, sort_keys=True, separators=(",", ":"), default=str)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class Entry:
31
+ """A single chained log entry."""
32
+
33
+ id: str
34
+ session_id: str
35
+ timestamp: float
36
+ event_type: str
37
+ data: dict[str, Any]
38
+ previous_hash: str
39
+ hash: str = field(default="")
40
+
41
+ def to_dict(self) -> dict[str, Any]:
42
+ return asdict(self)
43
+
44
+ @classmethod
45
+ def from_dict(cls, raw: dict[str, Any]) -> Entry:
46
+ return cls(**raw)
47
+
48
+ @property
49
+ def content_hash(self) -> str:
50
+ """Hash of this entry's content, excluding the stored `hash` field."""
51
+ content = {k: v for k, v in self.to_dict().items() if k != "hash"}
52
+ return hashlib.sha256(_canonical_json(content).encode("utf-8")).hexdigest()
53
+
54
+
55
+ def make_entry(
56
+ session_id: str,
57
+ event_type: str,
58
+ data: dict[str, Any],
59
+ previous_hash: str,
60
+ ) -> Entry:
61
+ """
62
+ Build a single chained log entry, with its hash computed and set.
63
+
64
+ Args:
65
+ session_id: identifier for the agent run / conversation this belongs to.
66
+ event_type: e.g. "tool_call", "model_call", "decision".
67
+ data: arbitrary JSON-serializable payload for this event.
68
+ previous_hash: hash of the prior entry in this session's chain
69
+ (use GENESIS_HASH for the first entry).
70
+
71
+ Returns:
72
+ A completed, hashed Entry.
73
+ """
74
+ entry = Entry(
75
+ id=str(uuid.uuid4()),
76
+ session_id=session_id,
77
+ timestamp=time.time(),
78
+ event_type=event_type,
79
+ data=data,
80
+ previous_hash=previous_hash,
81
+ hash="",
82
+ )
83
+ return Entry(**{**entry.to_dict(), "hash": entry.content_hash})
84
+
85
+
86
+ def verify_chain(entries: list[Entry]) -> tuple[bool, int | None]:
87
+ """
88
+ Verify a list of entries forms an unbroken, untampered hash chain.
89
+
90
+ The list is assumed to be in chronological order for a single session.
91
+ An empty list is considered valid (vacuously).
92
+
93
+ Returns:
94
+ (is_valid, index_of_first_break_or_none)
95
+ """
96
+ expected_previous = GENESIS_HASH
97
+
98
+ for i, entry in enumerate(entries):
99
+ if entry.previous_hash != expected_previous:
100
+ return False, i
101
+
102
+ if entry.content_hash != entry.hash:
103
+ return False, i
104
+
105
+ expected_previous = entry.hash
106
+
107
+ return True, None
@@ -0,0 +1,3 @@
1
+ from .__main__ import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,134 @@
1
+ """
2
+ VerityLedger CLI.
3
+
4
+ Lets you inspect, verify, and export logs from the terminal without
5
+ writing any Python. Built entirely on top of the public `verityledger`
6
+ API (Tracer/LocalStore) - the CLI has no logic of its own beyond
7
+ argument parsing and formatting.
8
+
9
+ Usage:
10
+ verityledger sessions [--log PATH]
11
+ verityledger show SESSION_ID [--log PATH]
12
+ verityledger verify SESSION_ID [--log PATH]
13
+ verityledger export SESSION_ID OUT_PATH [--log PATH]
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import sys
20
+ from datetime import datetime, timezone
21
+
22
+ from ..core import Tracer
23
+ from ..exceptions import ChainIntegrityError, SessionNotFoundError, VerityLedgerError
24
+
25
+ DEFAULT_LOG_PATH = "./verityledger_log.jsonl"
26
+
27
+
28
+ def _build_parser() -> argparse.ArgumentParser:
29
+ parser = argparse.ArgumentParser(
30
+ prog="verityledger",
31
+ description="Inspect, verify, and export VerityLedger agent logs.",
32
+ )
33
+ parser.add_argument(
34
+ "--log",
35
+ default=DEFAULT_LOG_PATH,
36
+ help=f"Path to the log file (default: {DEFAULT_LOG_PATH})",
37
+ )
38
+
39
+ subparsers = parser.add_subparsers(dest="command", required=True)
40
+
41
+ subparsers.add_parser("sessions", help="List all session ids in the log.")
42
+
43
+ show = subparsers.add_parser("show", help="Print all entries for a session.")
44
+ show.add_argument("session_id")
45
+
46
+ verify = subparsers.add_parser("verify", help="Check a session's hash chain for tampering.")
47
+ verify.add_argument("session_id")
48
+
49
+ export = subparsers.add_parser("export", help="Export a session as a JSON audit report.")
50
+ export.add_argument("session_id")
51
+ export.add_argument("out_path")
52
+
53
+ return parser
54
+
55
+
56
+ def _format_timestamp(ts: float) -> str:
57
+ return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
58
+
59
+
60
+ def cmd_sessions(tracer: Tracer) -> int:
61
+ sessions = tracer.store.all_sessions()
62
+ if not sessions:
63
+ print("No sessions found.")
64
+ return 0
65
+ for sid in sessions:
66
+ count = len(tracer.store.get_session(sid))
67
+ print(f"{sid} ({count} entries)")
68
+ return 0
69
+
70
+
71
+ def cmd_show(tracer: Tracer, session_id: str) -> int:
72
+ entries = tracer.store.get_session(session_id)
73
+ if not entries:
74
+ print(f"No entries found for session '{session_id}'.", file=sys.stderr)
75
+ return 1
76
+ for entry in entries:
77
+ print(f"[{_format_timestamp(entry.timestamp)}] {entry.event_type}")
78
+ for key, value in entry.data.items():
79
+ print(f" {key}: {value}")
80
+ print(f" hash: {entry.hash[:12]}...")
81
+ return 0
82
+
83
+
84
+ def cmd_verify(tracer: Tracer, session_id: str) -> int:
85
+ try:
86
+ tracer.assert_valid(session_id)
87
+ except SessionNotFoundError as exc:
88
+ print(str(exc), file=sys.stderr)
89
+ return 1
90
+ except ChainIntegrityError as exc:
91
+ print(f"TAMPERING DETECTED: {exc}", file=sys.stderr)
92
+ return 1
93
+
94
+ entry_count = len(tracer.store.get_session(session_id))
95
+ print(f"Chain valid: {entry_count} entries, no tampering detected.")
96
+ return 0
97
+
98
+
99
+ def cmd_export(tracer: Tracer, session_id: str, out_path: str) -> int:
100
+ try:
101
+ report = tracer.export_report(session_id, out_path)
102
+ except SessionNotFoundError as exc:
103
+ print(str(exc), file=sys.stderr)
104
+ return 1
105
+
106
+ status = "valid" if report["chain_valid"] else "TAMPERED"
107
+ print(f"Exported {report['entry_count']} entries to {out_path} (chain: {status}).")
108
+ return 0
109
+
110
+
111
+ def main(argv: list[str] | None = None) -> int:
112
+ parser = _build_parser()
113
+ args = parser.parse_args(argv)
114
+ tracer = Tracer(log_path=args.log)
115
+
116
+ try:
117
+ if args.command == "sessions":
118
+ return cmd_sessions(tracer)
119
+ if args.command == "show":
120
+ return cmd_show(tracer, args.session_id)
121
+ if args.command == "verify":
122
+ return cmd_verify(tracer, args.session_id)
123
+ if args.command == "export":
124
+ return cmd_export(tracer, args.session_id, args.out_path)
125
+ except VerityLedgerError as exc:
126
+ print(f"Error: {exc}", file=sys.stderr)
127
+ return 1
128
+
129
+ parser.print_help()
130
+ return 1
131
+
132
+
133
+ if __name__ == "__main__":
134
+ sys.exit(main())