taskfence 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.
Files changed (45) hide show
  1. taskfence-0.1.0/CHANGELOG.md +34 -0
  2. taskfence-0.1.0/CITATION.cff +26 -0
  3. taskfence-0.1.0/CONTRIBUTING.md +32 -0
  4. taskfence-0.1.0/LICENSE +21 -0
  5. taskfence-0.1.0/MANIFEST.in +5 -0
  6. taskfence-0.1.0/PKG-INFO +169 -0
  7. taskfence-0.1.0/README.md +139 -0
  8. taskfence-0.1.0/SECURITY.md +25 -0
  9. taskfence-0.1.0/docs/DESIGN.md +293 -0
  10. taskfence-0.1.0/docs/LIMITATIONS.md +22 -0
  11. taskfence-0.1.0/docs/THREAT_MODEL.md +65 -0
  12. taskfence-0.1.0/examples/01_quickstart_sqlite.py +53 -0
  13. taskfence-0.1.0/examples/03_mcp_server/README.md +57 -0
  14. taskfence-0.1.0/examples/03_mcp_server/admin.py +74 -0
  15. taskfence-0.1.0/examples/03_mcp_server/app.py +95 -0
  16. taskfence-0.1.0/examples/03_mcp_server/demo.py +88 -0
  17. taskfence-0.1.0/examples/03_mcp_server/server.py +119 -0
  18. taskfence-0.1.0/pyproject.toml +46 -0
  19. taskfence-0.1.0/setup.cfg +4 -0
  20. taskfence-0.1.0/src/taskfence/__init__.py +11 -0
  21. taskfence-0.1.0/src/taskfence/_hooks.py +32 -0
  22. taskfence-0.1.0/src/taskfence/fence.py +200 -0
  23. taskfence-0.1.0/src/taskfence/outcomes.py +93 -0
  24. taskfence-0.1.0/src/taskfence/stores/__init__.py +3 -0
  25. taskfence-0.1.0/src/taskfence/stores/postgres.py +328 -0
  26. taskfence-0.1.0/src/taskfence/stores/sqlite.py +310 -0
  27. taskfence-0.1.0/src/taskfence/testing/__init__.py +8 -0
  28. taskfence-0.1.0/src/taskfence/testing/audit.py +81 -0
  29. taskfence-0.1.0/src/taskfence/testing/latches.py +138 -0
  30. taskfence-0.1.0/src/taskfence/testing/plugin.py +15 -0
  31. taskfence-0.1.0/src/taskfence/tokens.py +120 -0
  32. taskfence-0.1.0/src/taskfence.egg-info/PKG-INFO +169 -0
  33. taskfence-0.1.0/src/taskfence.egg-info/SOURCES.txt +43 -0
  34. taskfence-0.1.0/src/taskfence.egg-info/dependency_links.txt +1 -0
  35. taskfence-0.1.0/src/taskfence.egg-info/entry_points.txt +2 -0
  36. taskfence-0.1.0/src/taskfence.egg-info/requires.txt +9 -0
  37. taskfence-0.1.0/src/taskfence.egg-info/top_level.txt +1 -0
  38. taskfence-0.1.0/tests/conftest.py +120 -0
  39. taskfence-0.1.0/tests/test_federated_outbox.py +70 -0
  40. taskfence-0.1.0/tests/test_guard.py +174 -0
  41. taskfence-0.1.0/tests/test_mcp_example.py +38 -0
  42. taskfence-0.1.0/tests/test_pg_isolation.py +136 -0
  43. taskfence-0.1.0/tests/test_races.py +236 -0
  44. taskfence-0.1.0/tests/test_tokens.py +73 -0
  45. taskfence-0.1.0/tests/unsafe_baselines.py +38 -0
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes are listed here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/). Before 1.0, minor versions may change the API.
4
+
5
+ ## [0.1.0] - 2026-10-31
6
+
7
+ The first release: a library implementation of the commit-time authorization design from *TaskFence: Commit-Time Authorization and Revocation Safety for Long-Running MCP Agent Workflows* (ICICyTA 2026).
8
+
9
+ ### Added
10
+ - **The `Fence` API.**
11
+ - `grant`, `revoke`, `regrant` and `issue` (signed task tokens)
12
+ - `guard()`, which authorizes an effect inside the transaction that commits it, plus the `@fenced` decorator
13
+ - `cancel_task` and `cancel_operation` fences
14
+ - idempotent `operation_id`s, with an explicit `Duplicate` outcome
15
+ - an optional application predicate, checked inside the same transaction
16
+ - a transactional outbox: `enqueue` and `drain_outbox`
17
+ - owner-side federated messages: `apply_install` and `apply_fence`, idempotent and with a monotone floor
18
+ - **Tokens.** `tf1` format, HMAC-SHA256, and a `KeyRing` with key rotation. Standard library only; no runtime dependencies.
19
+ - **SQLite store.** WAL mode; the fence check is a conditional UPDATE under the write lock, as evaluated in the paper.
20
+ - **PostgreSQL store** (`taskfence[postgres]`), with two lock modes:
21
+ - `lock="shared"` (default): a fair advisory lock plus `FOR SHARE`
22
+ - `lock="exclusive"`: `FOR UPDATE`
23
+
24
+ It is safe at READ COMMITTED, REPEATABLE READ and SERIALIZABLE, and is tested in CI on Postgres 15, 16 and 17.
25
+ - **`taskfence.testing`.** Latches for deterministic races, an independent SQL auditor (`audit`, `assert_no_violations`), and a `taskfence_latches` pytest fixture.
26
+ - **Examples.** A SQLite quickstart, and an MCP refund-desk server with a demo client (`taskfence[examples]`).
27
+ - **Docs.** Design, threat model and limitations.
28
+
29
+ ### Known limitations
30
+ See [docs/LIMITATIONS.md](docs/LIMITATIONS.md). In short:
31
+ - the effect must be in the same database as TaskFence's tables
32
+ - external calls are covered only as far as the outbox
33
+ - there is no async API yet
34
+ - there is no MCP Tasks extension integration yet
@@ -0,0 +1,26 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use TaskFence, please cite the paper below."
3
+ title: "TaskFence: revocation-safe side effects for long-running AI agent tasks"
4
+ type: software
5
+ authors:
6
+ - family-names: Mohammad
7
+ given-names: Muzeeb
8
+ license: MIT
9
+ version: 0.1.0
10
+ repository-code: "https://github.com/mohdmuzeeb/taskfence"
11
+ keywords:
12
+ - authorization
13
+ - revocation
14
+ - AI agents
15
+ - Model Context Protocol
16
+ - fencing tokens
17
+ # TODO: add `doi:` for the archived research artifact (Zenodo) once it is reserved.
18
+ preferred-citation:
19
+ type: conference-paper
20
+ title: "TaskFence: Commit-Time Authorization and Revocation Safety for Long-Running MCP Agent Workflows"
21
+ authors:
22
+ - family-names: Mohammad
23
+ given-names: Muzeeb
24
+ collection-title: "International Conference on Intelligent Cybernetics Technology & Applications (ICICyTA 2026)"
25
+ year: 2026
26
+ # TODO: add `doi:` and pages once the proceedings are published.
@@ -0,0 +1,32 @@
1
+ # Contributing
2
+
3
+ Thanks for helping. Issues, bug reports and pull requests are welcome.
4
+
5
+ ## Development setup
6
+
7
+ ```bash
8
+ python3 -m venv .venv && .venv/bin/pip install -e ".[test,postgres,examples]"
9
+ ```
10
+
11
+ ```bash
12
+ .venv/bin/python -m pytest # SQLite + MCP example
13
+ ```
14
+
15
+ PostgreSQL tests need a server with commit timestamps enabled:
16
+
17
+ ```bash
18
+ docker run -d --rm --name taskfence-pg -e POSTGRES_PASSWORD=test -p 127.0.0.1:55432:5432 postgres:16 -c track_commit_timestamp=on
19
+ ```
20
+
21
+ ```bash
22
+ TASKFENCE_PG_DSN=postgresql://postgres:test@127.0.0.1:55432/postgres .venv/bin/python -m pytest
23
+ ```
24
+
25
+ ## Ground rules for changes
26
+
27
+ - **Safety changes come with a race test.** Anything touching `guard()`, a store, or the control plane needs a latch-scheduled test in `tests/test_races.py`, or `tests/test_pg_isolation.py` for PostgreSQL-specific behavior. The test should fail without the change, and `assert_no_violations()` should pass with it.
28
+ - **Every back-end runs the same tests.** New behavior is tested through `conftest.py`'s `backend` fixture, so it runs on SQLite, `pg-shared` and `pg-exclusive`.
29
+ - **The core has no runtime dependencies.** The core uses only the standard library; drivers are optional extras.
30
+ - **Claims match evidence.** If a change affects what TaskFence guarantees, update `docs/THREAT_MODEL.md` and `docs/LIMITATIONS.md` in the same pull request.
31
+
32
+ Report security issues privately; see [SECURITY.md](SECURITY.md).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Muzeeb Mohammad
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,5 @@
1
+ include LICENSE README.md CHANGELOG.md CITATION.cff SECURITY.md CONTRIBUTING.md
2
+ recursive-include tests *.py
3
+ recursive-include examples *.py *.md
4
+ recursive-include docs *.md
5
+ global-exclude __pycache__ *.py[cod] *.sqlite*
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: taskfence
3
+ Version: 0.1.0
4
+ Summary: Revocation-safe side effects for long-running AI agent tasks: commit-time authorization in your own database transaction.
5
+ Author: Muzeeb Mohammad
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mohdmuzeeb/taskfence
8
+ Project-URL: Documentation, https://github.com/mohdmuzeeb/taskfence/tree/main/docs
9
+ Project-URL: Issues, https://github.com/mohdmuzeeb/taskfence/issues
10
+ Project-URL: Changelog, https://github.com/mohdmuzeeb/taskfence/blob/main/CHANGELOG.md
11
+ Keywords: ai-agents,mcp,model-context-protocol,authorization,revocation,fencing-token
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Framework :: Pytest
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Database
20
+ Requires-Python: >=3.12
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest>=8; extra == "test"
25
+ Provides-Extra: postgres
26
+ Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
27
+ Provides-Extra: examples
28
+ Requires-Dist: mcp>=2.3; extra == "examples"
29
+ Dynamic: license-file
30
+
31
+ # TaskFence
32
+
33
+ [![CI](https://github.com/mohdmuzeeb/taskfence/actions/workflows/ci.yml/badge.svg)](https://github.com/mohdmuzeeb/taskfence/actions/workflows/ci.yml)
34
+ [![PyPI](https://img.shields.io/pypi/v/taskfence)](https://pypi.org/project/taskfence/)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/mohdmuzeeb/taskfence/blob/main/LICENSE)
36
+
37
+ **Revocation-safe side effects for long-running AI agent tasks.**
38
+
39
+ If a user revokes permission while an agent task is running, TaskFence guarantees the task cannot commit its side effect afterwards. The check runs **inside your own database transaction**, at commit time, not before it.
40
+
41
+ ## The problem
42
+
43
+ 1. An agent task is admitted: the user has granted `refund.issue`.
44
+ 2. The task works for 10 minutes (LLM calls, tool calls, waiting for input).
45
+ 3. At minute 4, the user revokes the grant.
46
+ 4. At minute 10, the task writes the refund. **Most servers allow this**, because they checked permission when the task started, or just before the write in a separate step.
47
+
48
+ The TaskFence paper measured this window. In one randomized run, designs that check at start or in a separate pre-commit step let 317 provable commits through after revocation. Checking inside the commit transaction let none through.
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install taskfence # SQLite; core has no dependencies
54
+ ```
55
+
56
+ ```bash
57
+ pip install "taskfence[postgres]" # PostgreSQL via psycopg 3
58
+ ```
59
+
60
+ Requires Python 3.12+. PostgreSQL 15, 16 and 17 are tested in CI.
61
+
62
+ ## Example
63
+
64
+ ```python
65
+ from taskfence import Denied, Fence, KeyRing
66
+ from taskfence.stores.sqlite import connect
67
+
68
+ fence = Fence(KeyRing.from_env("TASKFENCE_KEYS")) # or KeyRing.generate() to try it out
69
+ conn = connect("app.sqlite")
70
+ fence.migrate(conn)
71
+ conn.executescript("CREATE TABLE IF NOT EXISTS orders (id TEXT PRIMARY KEY, refunded INTEGER DEFAULT 0);"
72
+ "INSERT OR IGNORE INTO orders VALUES ('order:9001', 0);")
73
+
74
+ # The user grants the agent permission; a task is admitted with a signed token.
75
+ g = fence.grant(conn, principal="user:42", operation="refund.issue", resources=["order:9001"])
76
+ token = fence.issue(conn, g.grant_id, task_id="task-1")
77
+
78
+ # ... the task works for minutes; meanwhile the user may call fence.revoke(conn, g.grant_id) ...
79
+
80
+ try:
81
+ with fence.transaction(conn): # do slow work BEFORE opening the transaction
82
+ fence.guard(conn, token, operation="refund.issue", resource="order:9001",
83
+ operation_id="task-1:refund")
84
+ conn.execute("UPDATE orders SET refunded = 1 WHERE id = 'order:9001'")
85
+ except Denied as e:
86
+ print("blocked:", e.code) # STALE_EPOCH if access was revoked meanwhile
87
+ ```
88
+
89
+ [examples/01_quickstart_sqlite.py](https://github.com/mohdmuzeeb/taskfence/blob/main/examples/01_quickstart_sqlite.py) runs this with a revocation in the middle and prints `refund blocked: STALE_EPOCH`.
90
+
91
+ For PostgreSQL:
92
+
93
+ ```python
94
+ from taskfence.stores.postgres import PostgresStore, connect
95
+ fence = Fence(KeyRing.from_env(), PostgresStore()) # fair shared locking per grant (default)
96
+ ```
97
+
98
+ ## How it works
99
+
100
+ - **Grants have epochs.** Revoking a grant raises a fence (`min_epoch`) in your database. Regranting starts a new epoch.
101
+ - **Tasks carry a signed token** naming the epoch they were admitted under.
102
+ - **`guard()` checks the token in the same transaction as the effect.** The commit either sees the revocation, or is strictly ordered before it.
103
+ - **The same check also:**
104
+ - fences cancelled tasks
105
+ - makes each `operation_id` commit at most once
106
+ - enforces resource scope and an optional application predicate
107
+ - can write an outbox entry for external calls
108
+
109
+ Details: [docs/DESIGN.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/DESIGN.md) and [docs/THREAT_MODEL.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/THREAT_MODEL.md).
110
+
111
+ ## What it guarantees, and what it does not
112
+
113
+ - ✅ No effect protected by `guard()` commits after revocation, or after task cancellation, has taken effect.
114
+ - ✅ A revocation waits for at most the guarded transactions already in flight, never for a whole task.
115
+ - ❌ Not retroactive: effects that committed before the revocation stay committed.
116
+ - ❌ External calls (payments, e-mail) are covered only as far as the outbox entry. Your downstream must be idempotent.
117
+ - ❌ The effect must be in the same database as TaskFence's tables. Federated multi-service mode is planned for v0.2.
118
+
119
+ Read [docs/LIMITATIONS.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/LIMITATIONS.md) before relying on it.
120
+
121
+ ## Testing your own code for revocation races
122
+
123
+ `taskfence.testing` stages races deterministically with latches, and checks the database with an independent auditor:
124
+
125
+ ```python
126
+ from taskfence.testing import assert_no_violations, hook, run_in_thread
127
+
128
+ # `taskfence_latches` is a fixture that ships with taskfence; `fence`, `conn` and `grant` are yours.
129
+ def test_revoke_while_refund_runs(taskfence_latches, fence, conn, grant):
130
+ token = fence.issue(conn, grant.grant_id, task_id="t1")
131
+ pause = taskfence_latches.pause_at("app.before_txn") # your code calls hook("app.before_txn")
132
+ worker = run_in_thread(lambda: my_refund(token))
133
+ pause.wait_reached()
134
+ fence.revoke(conn, grant.grant_id) # revoke while the task is mid-flight
135
+ pause.release()
136
+ worker.join()
137
+ assert_no_violations(conn) # no commit after the revocation
138
+ ```
139
+
140
+ ## Using with MCP servers
141
+
142
+ [examples/03_mcp_server](https://github.com/mohdmuzeeb/taskfence/tree/main/examples/03_mcp_server) is a refund-desk MCP server whose background refunds are fenced by TaskFence. A demo drives it with the official MCP client and shows four cases:
143
+ - a refund revoked mid-task is blocked
144
+ - a refund cancelled mid-task is blocked
145
+ - a refund on another customer's order is blocked
146
+ - a refund after a fresh grant commits
147
+
148
+ The Python MCP SDK does not include the MCP Tasks extension yet, so the example uses start-then-poll tools.
149
+
150
+ ## Roadmap
151
+
152
+ - **v0.2:** a federated grant authority (signed service-to-service RPC), TypeScript middleware for the MCP Tasks extension, and an async API
153
+ - **v0.3:** `taskfence scan`, a revocation-race checker for MCP servers
154
+
155
+ ## Research and citation
156
+
157
+ TaskFence is based on peer-reviewed research:
158
+
159
+ > M. Mohammad, "TaskFence: Commit-Time Authorization and Revocation Safety for Long-Running MCP Agent Workflows," *International Conference on Intelligent Cybernetics Technology & Applications (ICICyTA 2026)*.
160
+
161
+ If you use TaskFence, please cite the paper. GitHub's "Cite this repository" button uses [CITATION.cff](https://github.com/mohdmuzeeb/taskfence/blob/main/CITATION.cff). The archived research artifact (code, preregistration, wire traces, raw databases) will be linked here once it has a DOI.
162
+
163
+ ## Contributing and security
164
+
165
+ Issues and pull requests are welcome; see [CONTRIBUTING.md](https://github.com/mohdmuzeeb/taskfence/blob/main/CONTRIBUTING.md). Report vulnerabilities privately as described in [SECURITY.md](https://github.com/mohdmuzeeb/taskfence/blob/main/SECURITY.md).
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,139 @@
1
+ # TaskFence
2
+
3
+ [![CI](https://github.com/mohdmuzeeb/taskfence/actions/workflows/ci.yml/badge.svg)](https://github.com/mohdmuzeeb/taskfence/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/taskfence)](https://pypi.org/project/taskfence/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/mohdmuzeeb/taskfence/blob/main/LICENSE)
6
+
7
+ **Revocation-safe side effects for long-running AI agent tasks.**
8
+
9
+ If a user revokes permission while an agent task is running, TaskFence guarantees the task cannot commit its side effect afterwards. The check runs **inside your own database transaction**, at commit time, not before it.
10
+
11
+ ## The problem
12
+
13
+ 1. An agent task is admitted: the user has granted `refund.issue`.
14
+ 2. The task works for 10 minutes (LLM calls, tool calls, waiting for input).
15
+ 3. At minute 4, the user revokes the grant.
16
+ 4. At minute 10, the task writes the refund. **Most servers allow this**, because they checked permission when the task started, or just before the write in a separate step.
17
+
18
+ The TaskFence paper measured this window. In one randomized run, designs that check at start or in a separate pre-commit step let 317 provable commits through after revocation. Checking inside the commit transaction let none through.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install taskfence # SQLite; core has no dependencies
24
+ ```
25
+
26
+ ```bash
27
+ pip install "taskfence[postgres]" # PostgreSQL via psycopg 3
28
+ ```
29
+
30
+ Requires Python 3.12+. PostgreSQL 15, 16 and 17 are tested in CI.
31
+
32
+ ## Example
33
+
34
+ ```python
35
+ from taskfence import Denied, Fence, KeyRing
36
+ from taskfence.stores.sqlite import connect
37
+
38
+ fence = Fence(KeyRing.from_env("TASKFENCE_KEYS")) # or KeyRing.generate() to try it out
39
+ conn = connect("app.sqlite")
40
+ fence.migrate(conn)
41
+ conn.executescript("CREATE TABLE IF NOT EXISTS orders (id TEXT PRIMARY KEY, refunded INTEGER DEFAULT 0);"
42
+ "INSERT OR IGNORE INTO orders VALUES ('order:9001', 0);")
43
+
44
+ # The user grants the agent permission; a task is admitted with a signed token.
45
+ g = fence.grant(conn, principal="user:42", operation="refund.issue", resources=["order:9001"])
46
+ token = fence.issue(conn, g.grant_id, task_id="task-1")
47
+
48
+ # ... the task works for minutes; meanwhile the user may call fence.revoke(conn, g.grant_id) ...
49
+
50
+ try:
51
+ with fence.transaction(conn): # do slow work BEFORE opening the transaction
52
+ fence.guard(conn, token, operation="refund.issue", resource="order:9001",
53
+ operation_id="task-1:refund")
54
+ conn.execute("UPDATE orders SET refunded = 1 WHERE id = 'order:9001'")
55
+ except Denied as e:
56
+ print("blocked:", e.code) # STALE_EPOCH if access was revoked meanwhile
57
+ ```
58
+
59
+ [examples/01_quickstart_sqlite.py](https://github.com/mohdmuzeeb/taskfence/blob/main/examples/01_quickstart_sqlite.py) runs this with a revocation in the middle and prints `refund blocked: STALE_EPOCH`.
60
+
61
+ For PostgreSQL:
62
+
63
+ ```python
64
+ from taskfence.stores.postgres import PostgresStore, connect
65
+ fence = Fence(KeyRing.from_env(), PostgresStore()) # fair shared locking per grant (default)
66
+ ```
67
+
68
+ ## How it works
69
+
70
+ - **Grants have epochs.** Revoking a grant raises a fence (`min_epoch`) in your database. Regranting starts a new epoch.
71
+ - **Tasks carry a signed token** naming the epoch they were admitted under.
72
+ - **`guard()` checks the token in the same transaction as the effect.** The commit either sees the revocation, or is strictly ordered before it.
73
+ - **The same check also:**
74
+ - fences cancelled tasks
75
+ - makes each `operation_id` commit at most once
76
+ - enforces resource scope and an optional application predicate
77
+ - can write an outbox entry for external calls
78
+
79
+ Details: [docs/DESIGN.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/DESIGN.md) and [docs/THREAT_MODEL.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/THREAT_MODEL.md).
80
+
81
+ ## What it guarantees, and what it does not
82
+
83
+ - ✅ No effect protected by `guard()` commits after revocation, or after task cancellation, has taken effect.
84
+ - ✅ A revocation waits for at most the guarded transactions already in flight, never for a whole task.
85
+ - ❌ Not retroactive: effects that committed before the revocation stay committed.
86
+ - ❌ External calls (payments, e-mail) are covered only as far as the outbox entry. Your downstream must be idempotent.
87
+ - ❌ The effect must be in the same database as TaskFence's tables. Federated multi-service mode is planned for v0.2.
88
+
89
+ Read [docs/LIMITATIONS.md](https://github.com/mohdmuzeeb/taskfence/blob/main/docs/LIMITATIONS.md) before relying on it.
90
+
91
+ ## Testing your own code for revocation races
92
+
93
+ `taskfence.testing` stages races deterministically with latches, and checks the database with an independent auditor:
94
+
95
+ ```python
96
+ from taskfence.testing import assert_no_violations, hook, run_in_thread
97
+
98
+ # `taskfence_latches` is a fixture that ships with taskfence; `fence`, `conn` and `grant` are yours.
99
+ def test_revoke_while_refund_runs(taskfence_latches, fence, conn, grant):
100
+ token = fence.issue(conn, grant.grant_id, task_id="t1")
101
+ pause = taskfence_latches.pause_at("app.before_txn") # your code calls hook("app.before_txn")
102
+ worker = run_in_thread(lambda: my_refund(token))
103
+ pause.wait_reached()
104
+ fence.revoke(conn, grant.grant_id) # revoke while the task is mid-flight
105
+ pause.release()
106
+ worker.join()
107
+ assert_no_violations(conn) # no commit after the revocation
108
+ ```
109
+
110
+ ## Using with MCP servers
111
+
112
+ [examples/03_mcp_server](https://github.com/mohdmuzeeb/taskfence/tree/main/examples/03_mcp_server) is a refund-desk MCP server whose background refunds are fenced by TaskFence. A demo drives it with the official MCP client and shows four cases:
113
+ - a refund revoked mid-task is blocked
114
+ - a refund cancelled mid-task is blocked
115
+ - a refund on another customer's order is blocked
116
+ - a refund after a fresh grant commits
117
+
118
+ The Python MCP SDK does not include the MCP Tasks extension yet, so the example uses start-then-poll tools.
119
+
120
+ ## Roadmap
121
+
122
+ - **v0.2:** a federated grant authority (signed service-to-service RPC), TypeScript middleware for the MCP Tasks extension, and an async API
123
+ - **v0.3:** `taskfence scan`, a revocation-race checker for MCP servers
124
+
125
+ ## Research and citation
126
+
127
+ TaskFence is based on peer-reviewed research:
128
+
129
+ > M. Mohammad, "TaskFence: Commit-Time Authorization and Revocation Safety for Long-Running MCP Agent Workflows," *International Conference on Intelligent Cybernetics Technology & Applications (ICICyTA 2026)*.
130
+
131
+ If you use TaskFence, please cite the paper. GitHub's "Cite this repository" button uses [CITATION.cff](https://github.com/mohdmuzeeb/taskfence/blob/main/CITATION.cff). The archived research artifact (code, preregistration, wire traces, raw databases) will be linked here once it has a DOI.
132
+
133
+ ## Contributing and security
134
+
135
+ Issues and pull requests are welcome; see [CONTRIBUTING.md](https://github.com/mohdmuzeeb/taskfence/blob/main/CONTRIBUTING.md). Report vulnerabilities privately as described in [SECURITY.md](https://github.com/mohdmuzeeb/taskfence/blob/main/SECURITY.md).
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,25 @@
1
+ # Security policy
2
+
3
+ TaskFence is a security library, so reports are very welcome.
4
+
5
+ ## Reporting a vulnerability
6
+
7
+ Please **do not open a public issue**. Report privately through GitHub's private vulnerability reporting: the **Security** tab of this repository, then **Report a vulnerability**.
8
+
9
+ Include:
10
+ - the TaskFence version
11
+ - the store (SQLite or PostgreSQL and its version)
12
+ - the isolation level
13
+ - a minimal reproduction, ideally a test using `taskfence.testing` latches and `audit()`
14
+
15
+ You should get an acknowledgment within 7 days. Fixes for confirmed issues are released with a GitHub security advisory that credits the reporter, unless they prefer otherwise.
16
+
17
+ ## What counts
18
+
19
+ Anything that lets an effect commit after its revocation or cancellation is effective, as defined in [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md). Also reportable: token forgery, a scope or principal bypass, or a way to make the auditor miss a violation.
20
+
21
+ The limitations listed in [docs/LIMITATIONS.md](docs/LIMITATIONS.md) are known and documented, not vulnerabilities.
22
+
23
+ ## Supported versions
24
+
25
+ Only the latest release receives fixes during the 0.x series.