interop-verifier 1.0.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 (26) hide show
  1. interop_verifier-1.0.0/.dockerignore +10 -0
  2. interop_verifier-1.0.0/.github/workflows/ci.yml +39 -0
  3. interop_verifier-1.0.0/.github/workflows/docker.yml +25 -0
  4. interop_verifier-1.0.0/.gitignore +8 -0
  5. interop_verifier-1.0.0/Dockerfile +26 -0
  6. interop_verifier-1.0.0/Makefile +34 -0
  7. interop_verifier-1.0.0/PKG-INFO +79 -0
  8. interop_verifier-1.0.0/README.md +57 -0
  9. interop_verifier-1.0.0/pyproject.toml +45 -0
  10. interop_verifier-1.0.0/scripts/interop-verifier-incept-no-wits.json +9 -0
  11. interop_verifier-1.0.0/src/interop_verifier/__init__.py +3 -0
  12. interop_verifier-1.0.0/src/interop_verifier/app/__init__.py +1 -0
  13. interop_verifier-1.0.0/src/interop_verifier/app/cli.py +107 -0
  14. interop_verifier-1.0.0/src/interop_verifier/core/__init__.py +1 -0
  15. interop_verifier-1.0.0/src/interop_verifier/core/basing.py +54 -0
  16. interop_verifier-1.0.0/src/interop_verifier/core/credentials.py +32 -0
  17. interop_verifier-1.0.0/src/interop_verifier/core/handling.py +208 -0
  18. interop_verifier-1.0.0/src/interop_verifier/core/httping.py +90 -0
  19. interop_verifier-1.0.0/src/interop_verifier/core/monitoring.py +23 -0
  20. interop_verifier-1.0.0/src/interop_verifier/core/policy.py +97 -0
  21. interop_verifier-1.0.0/src/interop_verifier/core/serving.py +270 -0
  22. interop_verifier-1.0.0/src/interop_verifier/core/verifying.py +40 -0
  23. interop_verifier-1.0.0/src/interop_verifier/data/__init__.py +1 -0
  24. interop_verifier-1.0.0/src/interop_verifier/data/interop-verifier-incept-no-wits.json +9 -0
  25. interop_verifier-1.0.0/tests/test_policy.py +90 -0
  26. interop_verifier-1.0.0/uv.lock +1230 -0
@@ -0,0 +1,10 @@
1
+ .git
2
+ .github
3
+ .idea
4
+ .pytest_cache
5
+ __pycache__
6
+ *.py[cod]
7
+ build
8
+ dist
9
+ *.egg-info
10
+ .venv
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ tags: ["*"]
7
+ pull_request:
8
+ branches: ["master"]
9
+
10
+ jobs:
11
+ test:
12
+ name: Python ${{ matrix.python-version }}
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.12"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v6
24
+ with:
25
+ version: "0.8.5"
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v6
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+
32
+ - name: Install dependencies
33
+ run: uv sync --dev --frozen
34
+
35
+ - name: Run checks
36
+ run: make check
37
+
38
+ - name: Build package
39
+ run: make build
@@ -0,0 +1,25 @@
1
+ name: Docker
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ tags: ["*"]
7
+ pull_request:
8
+ branches: ["master"]
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Docker Buildx
18
+ uses: docker/setup-buildx-action@v3
19
+
20
+ - name: Build image
21
+ uses: docker/build-push-action@v6
22
+ with:
23
+ context: .
24
+ push: false
25
+ tags: interop-verifier:ci
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+ .idea/
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ *.py[cod]
@@ -0,0 +1,26 @@
1
+ FROM python:3.12-slim AS runtime
2
+
3
+ COPY --from=ghcr.io/astral-sh/uv:0.8.5 /uv /uvx /bin/
4
+
5
+ ENV PYTHONDONTWRITEBYTECODE=1 \
6
+ PYTHONUNBUFFERED=1 \
7
+ UV_COMPILE_BYTECODE=1 \
8
+ UV_LINK_MODE=copy \
9
+ PATH="/app/.venv/bin:$PATH"
10
+
11
+ WORKDIR /app
12
+
13
+ RUN apt-get update \
14
+ && apt-get install -y --no-install-recommends ca-certificates libsodium23 \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ COPY pyproject.toml uv.lock README.md ./
18
+ COPY src ./src
19
+ COPY scripts ./scripts
20
+
21
+ RUN uv sync --frozen --no-dev
22
+
23
+ EXPOSE 9723
24
+
25
+ ENTRYPOINT ["interop-verifier"]
26
+ CMD ["--help"]
@@ -0,0 +1,34 @@
1
+ IMAGE ?= kentbull/interop-verifier
2
+ VERSION ?= 1.0.0
3
+
4
+ .PHONY: sync check test build clean docker-build docker-run docker-push publish-pypi
5
+
6
+ sync:
7
+ uv sync --dev
8
+
9
+ check:
10
+ uv run python -m compileall src tests
11
+ uv run pytest -q
12
+
13
+ test:
14
+ uv run pytest -q
15
+
16
+ build: clean
17
+ uv build
18
+ uv run twine check dist/*
19
+
20
+ clean:
21
+ rm -rf build dist *.egg-info
22
+
23
+ docker-build:
24
+ docker build -t $(IMAGE):$(VERSION) -t $(IMAGE):latest .
25
+
26
+ docker-run:
27
+ docker run --rm $(IMAGE):$(VERSION) version
28
+
29
+ docker-push:
30
+ docker push $(IMAGE):$(VERSION)
31
+ docker push $(IMAGE):latest
32
+
33
+ publish-pypi: build
34
+ uv run twine upload dist/*
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: interop-verifier
3
+ Version: 1.0.0
4
+ Summary: Generalized Sally-style KERIpy verifier for ACDC interop tests
5
+ Project-URL: Homepage, https://github.com/kentbull/interop-verifier
6
+ Project-URL: Repository, https://github.com/kentbull/interop-verifier
7
+ Project-URL: Issues, https://github.com/kentbull/interop-verifier/issues
8
+ Author-email: Kent Bull <kent@kentbull.com>
9
+ Keywords: acdc,cesr,ipex,keri,verifier
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Security :: Cryptography
16
+ Requires-Python: >=3.12.6
17
+ Requires-Dist: falcon==4.0.2
18
+ Requires-Dist: hio<0.7.0,>=0.6.14
19
+ Requires-Dist: http-sfv==0.9.9
20
+ Requires-Dist: keri<1.3.0,>=1.2.12
21
+ Description-Content-Type: text/markdown
22
+
23
+ # interop-verifier
24
+
25
+ `interop-verifier` is a generalized Sally-style KERIpy verifier used by the
26
+ `keri-ts` ACDC interop tests. It receives normal CESR/IPEX credential
27
+ presentations, verifies them with KERIpy VDR state, and posts generic webhook
28
+ payloads without hardcoding the vLEI credential chain.
29
+
30
+ ## Install
31
+
32
+ ```sh
33
+ uv tool install interop-verifier
34
+ ```
35
+
36
+ ## Run from PyPI
37
+
38
+ ```sh
39
+ interop-verifier start \
40
+ --name verifier \
41
+ --base "" \
42
+ --alias verifier \
43
+ --http 9723 \
44
+ --web-hook http://127.0.0.1:9923/
45
+ ```
46
+
47
+ The verifier uses a bundled no-witness inception config by default. Pass
48
+ `--incept-file` to use a custom KERIpy-style inception JSON file.
49
+
50
+ ## Run with Docker
51
+
52
+ ```sh
53
+ docker run --rm -p 9723:9723 kentbull/interop-verifier:1.0.0 start \
54
+ --name verifier \
55
+ --head-dir /data \
56
+ --alias verifier \
57
+ --http 9723 \
58
+ --web-hook http://host.docker.internal:9923/
59
+ ```
60
+
61
+ Mount `/data` to persist verifier key state.
62
+
63
+ ## Interfaces
64
+
65
+ The verifier serves:
66
+
67
+ - `GET /health` for liveness and queue counts.
68
+ - `GET /oobi/{aid}/controller` for KERI controller OOBI resolution.
69
+ - `PUT /` and `POST /` for CESR/KERI ingress.
70
+ - Sally-style signed webhook callbacks for verified credentials.
71
+
72
+ ## Development
73
+
74
+ ```sh
75
+ make sync
76
+ make check
77
+ make build
78
+ make docker-build
79
+ ```
@@ -0,0 +1,57 @@
1
+ # interop-verifier
2
+
3
+ `interop-verifier` is a generalized Sally-style KERIpy verifier used by the
4
+ `keri-ts` ACDC interop tests. It receives normal CESR/IPEX credential
5
+ presentations, verifies them with KERIpy VDR state, and posts generic webhook
6
+ payloads without hardcoding the vLEI credential chain.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ uv tool install interop-verifier
12
+ ```
13
+
14
+ ## Run from PyPI
15
+
16
+ ```sh
17
+ interop-verifier start \
18
+ --name verifier \
19
+ --base "" \
20
+ --alias verifier \
21
+ --http 9723 \
22
+ --web-hook http://127.0.0.1:9923/
23
+ ```
24
+
25
+ The verifier uses a bundled no-witness inception config by default. Pass
26
+ `--incept-file` to use a custom KERIpy-style inception JSON file.
27
+
28
+ ## Run with Docker
29
+
30
+ ```sh
31
+ docker run --rm -p 9723:9723 kentbull/interop-verifier:1.0.0 start \
32
+ --name verifier \
33
+ --head-dir /data \
34
+ --alias verifier \
35
+ --http 9723 \
36
+ --web-hook http://host.docker.internal:9923/
37
+ ```
38
+
39
+ Mount `/data` to persist verifier key state.
40
+
41
+ ## Interfaces
42
+
43
+ The verifier serves:
44
+
45
+ - `GET /health` for liveness and queue counts.
46
+ - `GET /oobi/{aid}/controller` for KERI controller OOBI resolution.
47
+ - `PUT /` and `POST /` for CESR/KERI ingress.
48
+ - Sally-style signed webhook callbacks for verified credentials.
49
+
50
+ ## Development
51
+
52
+ ```sh
53
+ make sync
54
+ make check
55
+ make build
56
+ make docker-build
57
+ ```
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "interop-verifier"
3
+ version = "1.0.0"
4
+ description = "Generalized Sally-style KERIpy verifier for ACDC interop tests"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12.6"
7
+ authors = [
8
+ { name = "Kent Bull", email = "kent@kentbull.com" },
9
+ ]
10
+ keywords = ["keri", "acdc", "cesr", "ipex", "verifier"]
11
+ classifiers = [
12
+ "Development Status :: 5 - Production/Stable",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Topic :: Security :: Cryptography",
18
+ ]
19
+ dependencies = [
20
+ "keri>=1.2.12,<1.3.0",
21
+ "hio>=0.6.14,<0.7.0",
22
+ "falcon==4.0.2",
23
+ "http-sfv==0.9.9",
24
+ ]
25
+
26
+ [project.scripts]
27
+ interop-verifier = "interop_verifier.app.cli:main"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/kentbull/interop-verifier"
31
+ Repository = "https://github.com/kentbull/interop-verifier"
32
+ Issues = "https://github.com/kentbull/interop-verifier/issues"
33
+
34
+ [build-system]
35
+ requires = ["hatchling"]
36
+ build-backend = "hatchling.build"
37
+
38
+ [tool.hatchling.build.targets.wheel]
39
+ packages = ["src/interop_verifier"]
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "pytest>=8.3.5",
44
+ "twine>=6.2.0",
45
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "transferable": true,
3
+ "wits": [],
4
+ "toad": 0,
5
+ "icount": 1,
6
+ "ncount": 1,
7
+ "isith": "1",
8
+ "nsith": "1"
9
+ }
@@ -0,0 +1,3 @@
1
+ """Generalized Sally-style verifier for KERI interop tests."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1 @@
1
+ """CLI package for interop-verifier."""
@@ -0,0 +1,107 @@
1
+ """Command line entrypoint."""
2
+
3
+ import argparse
4
+ import json
5
+
6
+ from keri.app import configing, habbing, keeping
7
+
8
+ import interop_verifier
9
+ from interop_verifier.core import serving
10
+ from interop_verifier.core.policy import VerificationPolicy
11
+
12
+
13
+ def main():
14
+ parser = argparse.ArgumentParser(prog="interop-verifier")
15
+ sub = parser.add_subparsers(dest="command", required=True)
16
+ start = sub.add_parser("start", help="Start the interop verifier service")
17
+ start.add_argument("-p", "--http", default=9723, type=int)
18
+ start.add_argument("-n", "--name", default="interop-verifier")
19
+ start.add_argument("-b", "--base", default="")
20
+ start.add_argument("--head-dir", dest="headDirPath", default=None)
21
+ start.add_argument("-a", "--alias", required=True)
22
+ start.add_argument("-s", "--salt")
23
+ start.add_argument("--passcode", dest="bran", default=None)
24
+ start.add_argument("-i", "--incept-file", dest="inceptFile", default=None)
25
+ start.add_argument("-c", "--config-dir", dest="configDir", default=None)
26
+ start.add_argument("-f", "--config-file", dest="configFile", default=None)
27
+ start.add_argument("-w", "--web-hook", dest="webHook", required=True)
28
+ start.add_argument("--policy", dest="policyFile", default=None)
29
+ start.add_argument("--schema", dest="schemas", action="append", default=[])
30
+ start.add_argument("-r", "--retry-delay", default=3, type=int)
31
+ start.add_argument("-e", "--escrow-timeout", default=10, type=int)
32
+ start.add_argument("--no-clear-escrows", action="store_true", default=False)
33
+ start.set_defaults(handler=start_command)
34
+
35
+ version = sub.add_parser("version", help="Print version")
36
+ version.set_defaults(handler=lambda args: print(interop_verifier.__version__))
37
+
38
+ args = parser.parse_args()
39
+ args.handler(args)
40
+
41
+
42
+ def start_command(args):
43
+ hby = init_habery(
44
+ name=args.name,
45
+ base=args.base,
46
+ bran=args.bran,
47
+ config_file=args.configFile,
48
+ config_dir=args.configDir,
49
+ salt=args.salt,
50
+ head_dir_path=args.headDirPath,
51
+ )
52
+ incept_args = {
53
+ "name": args.name,
54
+ "base": args.base,
55
+ "alias": args.alias,
56
+ "bran": args.bran,
57
+ "incept_file": args.inceptFile,
58
+ "config_dir": args.configDir,
59
+ }
60
+ hab = serving.incept_if_new(hby, args.alias, incept_args)
61
+ serving.load_schemas(hby, args.schemas)
62
+ policy = VerificationPolicy(load_policy(args.policyFile))
63
+ doers = serving.setupDoers(
64
+ hby,
65
+ hab,
66
+ alias=args.alias,
67
+ http_port=args.http,
68
+ hook=args.webHook,
69
+ policy=policy,
70
+ timeout=args.escrow_timeout,
71
+ retry=args.retry_delay,
72
+ clear_escrows=not args.no_clear_escrows,
73
+ head_dir_path=args.headDirPath,
74
+ )
75
+ print(f"Interop verifier {hab.pre} listening on http://127.0.0.1:{args.http}", flush=True)
76
+ serving.run_doers(doers)
77
+
78
+
79
+ def init_habery(name, base, bran, config_file, config_dir, salt, head_dir_path=None):
80
+ ks = keeping.Keeper(name=name, base=base, temp=False, reopen=True, headDirPath=head_dir_path)
81
+ aeid = ks.gbls.get("aeid")
82
+ if aeid is None:
83
+ cf = None
84
+ if config_file is not None:
85
+ cf = configing.Configer(name=config_file, base=base, headDirPath=config_dir, temp=False, reopen=True, clear=False)
86
+ return habbing.Habery(
87
+ name=name,
88
+ base=base,
89
+ bran=bran,
90
+ cf=cf,
91
+ ks=ks,
92
+ salt=salt if salt else None,
93
+ headDirPath=head_dir_path,
94
+ )
95
+
96
+ return habbing.Habery(name=name, base=base, bran=bran, ks=ks, headDirPath=head_dir_path)
97
+
98
+
99
+ def load_policy(path):
100
+ if not path:
101
+ return {}
102
+ with open(path, "r", encoding="utf-8") as fp:
103
+ return json.load(fp)
104
+
105
+
106
+ if __name__ == "__main__":
107
+ main()
@@ -0,0 +1 @@
1
+ """Core verifier components."""
@@ -0,0 +1,54 @@
1
+ """Sally-style cue database for verifier communication state."""
2
+
3
+ from keri.core import coring, serdering
4
+ from keri.db import dbing, subing
5
+
6
+
7
+ class CueBaser(dbing.LMDBer):
8
+ """Stores verifier presentation, revocation, delivery, and ack queues."""
9
+
10
+ TailDirPath = "interop-verifier/db"
11
+ AltTailDirPath = ".interop-verifier/db"
12
+ TempPrefix = "interop_verifier_db_"
13
+
14
+ def __init__(self, name="cb", headDirPath=None, reopen=True, **kwa):
15
+ self.snd = None
16
+ self.iss = None
17
+ self.rev = None
18
+ self.recv = None
19
+ self.revk = None
20
+ self.ack = None
21
+ super().__init__(name=name, headDirPath=headDirPath, reopen=reopen, **kwa)
22
+
23
+ def reopen(self, **kwa):
24
+ super().reopen(**kwa)
25
+ self.snd = subing.CesrSuber(db=self, subkey="snd.", klas=coring.Prefixer)
26
+ self.iss = subing.CesrSuber(db=self, subkey="iss.", klas=coring.Dater)
27
+ self.rev = subing.CesrSuber(db=self, subkey="rev.", klas=coring.Dater)
28
+ self.recv = subing.SerderSuber(db=self, subkey="recv", klas=serdering.SerderACDC)
29
+ self.revk = subing.SerderSuber(db=self, subkey="revk", klas=serdering.SerderACDC)
30
+ self.ack = subing.SerderSuber(db=self, subkey="ack", klas=serdering.SerderACDC)
31
+ return self.env
32
+
33
+ def clearEscrows(self):
34
+ self.iss.trim()
35
+ self.rev.trim()
36
+ self.recv.trim()
37
+ self.revk.trim()
38
+ self.ack.trim()
39
+
40
+ @staticmethod
41
+ def items(suber, keys=""):
42
+ if hasattr(suber, "getItemIter"):
43
+ return suber.getItemIter(keys=keys)
44
+ return suber.getTopItemIter(keys=keys)
45
+
46
+ def getCounts(self):
47
+ return {
48
+ "senders": self.snd.cntAll(),
49
+ "iss": self.iss.cntAll(),
50
+ "rev": self.rev.cntAll(),
51
+ "recv": self.recv.cntAll(),
52
+ "revk": self.revk.cntAll(),
53
+ "ack": self.ack.cntAll(),
54
+ }
@@ -0,0 +1,32 @@
1
+ """TEL revocation cue bridge."""
2
+
3
+ from hio.base import doing
4
+ from hio.help import decking
5
+ from keri.core import coring
6
+
7
+
8
+ class TeveryCuery(doing.Doer):
9
+ """Moves KERIpy TEL revocation cues into the verifier cue database."""
10
+
11
+ def __init__(self, cdb, reger, cues=None, **kwa):
12
+ self.cdb = cdb
13
+ self.reger = reger
14
+ self.cues = cues if cues is not None else decking.Deck()
15
+ super().__init__(**kwa)
16
+
17
+ def do(self, tymth, *, tock=0.0, **opts):
18
+ self.wind(tymth)
19
+ self.tock = tock
20
+ yield self.tock
21
+ while True:
22
+ while self.cues:
23
+ cue = self.cues.popleft()
24
+ if cue.get("kin") == "revoked":
25
+ serder = cue["serder"]
26
+ said = serder.ked["i"]
27
+ creder = self.reger.creds.get(said)
28
+ if creder is not None:
29
+ self.cdb.snd.pin(keys=(said,), val=coring.Prefixer(qb64=creder.issuer))
30
+ self.cdb.rev.pin(keys=(said,), val=coring.Dater())
31
+ yield self.tock
32
+ yield self.tock