{"id":34852628,"url":"https://github.com/konnov/apalache-rpc-client","last_synced_at":"2026-04-01T17:38:41.345Z","repository":{"id":329449132,"uuid":"1094846391","full_name":"konnov/apalache-rpc-client","owner":"konnov","description":"Minimalistic Python client for interaction with the Apalache model checker over JSON RPC","archived":false,"fork":false,"pushed_at":"2025-11-18T10:49:26.000Z","size":85,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-22T06:49:19.314Z","etag":null,"topics":["apalache","client","model-based-testing","model-checking","quint","symbolic-execution","testing","tla","tlaplus"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/konnov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-12T08:52:31.000Z","updated_at":"2025-12-12T11:17:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/konnov/apalache-rpc-client","commit_stats":null,"previous_names":["konnov/apalache-rpc-client"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/konnov/apalache-rpc-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/konnov%2Fapalache-rpc-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/konnov%2Fapalache-rpc-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/konnov%2Fapalache-rpc-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/konnov%2Fapalache-rpc-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/konnov","download_url":"https://codeload.github.com/konnov/apalache-rpc-client/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/konnov%2Fapalache-rpc-client/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28035521,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-25T02:00:05.988Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apalache","client","model-based-testing","model-checking","quint","symbolic-execution","testing","tla","tlaplus"],"created_at":"2025-12-25T19:28:20.120Z","updated_at":"2026-04-01T17:38:41.327Z","avatar_url":"https://github.com/konnov.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Apalache RPC Client\n\n[![CI](https://github.com/konnov/apalache-rpc-client/actions/workflows/ci.yml/badge.svg)](https://github.com/konnov/apalache-rpc-client/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/konnov/apalache-rpc-client/branch/main/graph/badge.svg)](https://codecov.io/gh/konnov/apalache-rpc-client)\n[![PyPI version](https://badge.fury.io/py/apalache-rpc-client.svg)](https://badge.fury.io/py/apalache-rpc-client)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/github/license/konnov/apalache-rpc-client)](LICENSE)\n\nMinimalistic Python client for interaction with the Apalache model checker over\nJSON RPC.\n\n**Why?** Because now you can write your own test harnesses and symbolic search\ntools that interact with TLA\u003csup\u003e+\u003c/sup\u003e and Quint specifications. Use it with\nan AI agent, and you have got superpowers!\n\n**Minimalistic.** Connections usually require tinkering with the parameters.\nThis library only provides a thin wrapper around the JSON-RPC calls to the\nApalache server. Start with this library, to get the initial setup working.\nOnce, you have hit the limits, just fork this project and extend it to your\nneeds.\n\n**Compression.** The client compresses request bodies with gzip when they\nexceed 512 bytes and requests gzip-compressed responses from the server.\nThis is especially beneficial for `loadSpec` (base64-encoded source files)\nand `query` (full state/trace responses). Compression is enabled by default\nand can be disabled with `JsonRpcClient(compression=False)`.\n\n**Server API**. Refer to the [Apalache JSON-RPC API][] for the interaction\ndetails.\n\n## Installation\n\nYou can install the package via pip:\n\n```bash\npip install apalache-rpc-client\n```\n\nMake sure you have Apalache installed. Follow the [Apalache Installation Instructions][].\n\n## Usage\n\n### Install Apalache\n\n```shell\n# Install Java if you don't have it yet, e.g., on macOS:\nbrew install temurin@21\n# Download Apalache if you don't have it yet\ncurl -L -o apalache.tgz https://github.com/apalache-mc/apalache/releases/latest/download/apalache.tgz\ntar -xzf apalache.tgz\nexport APALACHE_HOME=\"$PWD/apalache\"\n```\n\n### Start and Stop the Apalache Server\n\n\u003c!-- name: test_server --\u003e\n```python\nimport logging\nimport sys\nimport time\nfrom tempfile import TemporaryDirectory\n\nfrom apalache_rpc.server import ApalacheServer\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n    handlers=[ logging.StreamHandler(sys.stdout) ]\n)\n\nwith TemporaryDirectory() as log_dir:\n    server = ApalacheServer(log_dir=log_dir, hostname=\"localhost\", port=18080)\n    assert server.start_server(), \"Failed to start the Apalache server\"\n    time.sleep(5)  # Let the server run for a bit\n    assert server.stop_server(), \"Failed to stop the Apalache server\"\n```\n\n### Call JSON-RPC Methods on the Circular Buffer Example\n\nThis example loads [`examples/circular-buffer/MC10u8_BuggyCircularBuffer.tla`](./examples/circular-buffer/MC10u8_BuggyCircularBuffer.tla)\nand the imported base module, chooses an initial state, queries an exported\noperator, checks invariants, explores a `Put` transition, compacts the current\nsymbolic trace, and rolls back to the initial snapshot.\n\n\u003c!-- name: test_circular_buffer_json_rpc --\u003e\n```python\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nfrom apalache_rpc.client import (\n    InvariantSatisfied,\n    JsonRpcClient,\n    NextModelFalse,\n    NextModelTrue,\n    TransitionEnabled,\n)\nfrom apalache_rpc.server import ApalacheServer\n\nrepo_root = Path.cwd()\nif not (repo_root / \"examples\").exists():\n    repo_root = repo_root.parent\n\nexamples_dir = repo_root / \"examples\" / \"circular-buffer\"\nsources = [\n    str(examples_dir / \"MC10u8_BuggyCircularBuffer.tla\"),\n    str(examples_dir / \"BuggyCircularBuffer.tla\"),\n]\n\nwith TemporaryDirectory() as log_dir:\n    server = ApalacheServer(log_dir=log_dir, hostname=\"localhost\", port=18081)\n    assert server.start_server(), \"Failed to start the Apalache server\"\n\n    try:\n        with JsonRpcClient(port=18081, solver_timeout=30) as client:\n            # Call loadSpec to register the TLA+ sources and named operators.\n            spec = client.load_spec(\n                sources=sources,\n                init=\"Init\",\n                next=\"Next\",\n                invariants=[\"SafeInv\"],\n                view=\"CountView\",\n            )\n            assert spec is not None\n            assert spec[\"next\"] == [\n                {\"index\": 0, \"labels\": [\"Put\"]},\n                {\"index\": 1, \"labels\": [\"Get\"]},\n            ]\n\n            # Call assumeTransition on the single initial transition.\n            init_status = client.assume_transition(spec[\"init\"][0][\"index\"])\n            assert isinstance(init_status, TransitionEnabled)\n\n            # Call nextStep to advance to the next frame (symbolic state).\n            # This does not fix any initial state!\n            init_snapshot = client.next_step()\n\n            # Call query with OPERATOR to evaluate the exported CountView operator.\n            count_view = client.query([\"OPERATOR\"], operator=\"CountView\")\n            assert count_view == {\"operatorValue\": {\"#tup\": [{\"#bigint\": \"0\"}]}}\n\n            # Call checkInvariant for each registered invariant through check_invariants.\n            inv_status = client.check_invariants(\n                len(spec[\"state\"]),\n                len(spec[\"action\"]),\n            )\n            assert isinstance(inv_status, InvariantSatisfied)\n\n            # Call assumeTransition on the Put action from the current state.\n            put_status = client.assume_transition(spec[\"next\"][0][\"index\"])\n            assert isinstance(put_status, TransitionEnabled)\n\n            # Call compact to collapse the current symbolic trace into one concrete state.\n            compacted_snapshot = client.compact(init_snapshot)\n            assert isinstance(compacted_snapshot, int)\n\n            # Call nextModel to ask whether CountView can change in the next state.\n            next_model = client.next_model(\"CountView\")\n            assert next_model[\"oldValue\"] == {\"#tup\": [{\"#bigint\": \"1\"}]}\n            assert isinstance(next_model[\"hasOld\"], NextModelTrue)\n            assert isinstance(next_model[\"hasNext\"], NextModelFalse)\n\n            # Call rollback to return to the saved initial snapshot.\n            client.rollback(init_snapshot)\n\n            # Call query again to confirm the rollback restored the old view.\n            assert client.query([\"OPERATOR\"], operator=\"CountView\") == count_view\n\n            # Call disposeSpec to release the server-side session explicitly.\n            client.dispose_spec()\n    finally:\n        assert server.stop_server(), \"Failed to stop the Apalache server\"\n```\n\n[Apalache JSON-RPC API]: https://github.com/apalache-mc/apalache/tree/main/json-rpc\n[Apalache Installation Instructions]: https://apalache-mc.org/docs/apalache/installation/index.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkonnov%2Fapalache-rpc-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkonnov%2Fapalache-rpc-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkonnov%2Fapalache-rpc-client/lists"}