pywrit 0.1.7__tar.gz → 0.1.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywrit
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log
5
5
  Project-URL: Homepage, https://withwrit.com
6
6
  Project-URL: Documentation, https://docs.withwrit.com
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  # Distribution name: pywrit (approved by Chad 2026-09-27).
7
7
  # The bare name "writ" is taken on PyPI.
8
8
  name = "pywrit"
9
- version = "0.1.7"
9
+ version = "0.1.8"
10
10
  description = "Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log"
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywrit
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log
5
5
  Project-URL: Homepage, https://withwrit.com
6
6
  Project-URL: Documentation, https://docs.withwrit.com
@@ -11573,6 +11573,7 @@ def main():
11573
11573
  policy.add_argument("--key", default=config.api_key())
11574
11574
  policy.add_argument("--set", nargs=2, metavar=("VERB", "MODE"), action="append", default=[])
11575
11575
  policy.add_argument("--env", default="", help="Environment override to get/set: dev, staging, or prod (default: tenant policy)")
11576
+ policy.add_argument("--principal", default="", help="Principal id for a per-principal policy override")
11576
11577
 
11577
11578
  check = sub.add_parser("check", help="ALLOW or DENY a write")
11578
11579
  check.add_argument("--key", default=config.api_key())
@@ -11603,7 +11604,8 @@ def main():
11603
11604
  revoked = sub.add_parser("revoked", help="List revoked principals (sponsor token)")
11604
11605
  revoked.add_argument("--sponsor-token", default=config.sponsor_token())
11605
11606
 
11606
- receipts = sub.add_parser("receipts", help="Latest receipts for your tenant")
11607
+ receipts = sub.add_parser("receipts", help="Latest receipts for your tenant, or one receipt by id")
11608
+ receipts.add_argument("receipt_id", nargs="?", default="", help="Fetch a single receipt by id; omit to list")
11607
11609
  receipts.add_argument("--key", default=config.api_key())
11608
11610
 
11609
11611
  stream = sub.add_parser("stream", help="Tail the live audit log (SSE stream)")
@@ -11718,6 +11720,90 @@ def main():
11718
11720
  gh_disc.add_argument("--totp-code", default="", help="TOTP step-up code")
11719
11721
  gh_disc.add_argument("--key", default=config.api_key())
11720
11722
 
11723
+ principals = sub.add_parser("principals", help="Manage the tenant identity directory")
11724
+ pr_sub = principals.add_subparsers(dest="principals_cmd", required=True)
11725
+ pr_list = pr_sub.add_parser("list", help="List principals")
11726
+ pr_list.add_argument("--key", default=config.api_key())
11727
+ pr_create = pr_sub.add_parser("create", help="Create a principal")
11728
+ pr_create.add_argument("name")
11729
+ pr_create.add_argument("--key", default=config.api_key())
11730
+ pr_rename = pr_sub.add_parser("rename", help="Rename a principal")
11731
+ pr_rename.add_argument("principal_id")
11732
+ pr_rename.add_argument("new_name")
11733
+ pr_rename.add_argument("--key", default=config.api_key())
11734
+ pr_disable = pr_sub.add_parser("disable", help="Disable a principal (pauses its keys)")
11735
+ pr_disable.add_argument("principal_id")
11736
+ pr_disable.add_argument("--key", default=config.api_key())
11737
+ pr_enable = pr_sub.add_parser("enable", help="Re-enable a principal")
11738
+ pr_enable.add_argument("principal_id")
11739
+ pr_enable.add_argument("--key", default=config.api_key())
11740
+ pr_del = pr_sub.add_parser("delete", help="Delete a principal")
11741
+ pr_del.add_argument("principal_id")
11742
+ pr_del.add_argument("--force", action="store_true", help="Revoke remaining keys first")
11743
+ pr_del.add_argument("--key", default=config.api_key())
11744
+ pr_rev = pr_sub.add_parser("revoke-keys", help="Revoke all API keys for a principal")
11745
+ pr_rev.add_argument("principal_id")
11746
+ pr_rev.add_argument("--key", default=config.api_key())
11747
+
11748
+ verbs = sub.add_parser("verbs", help="Manage the tenant verb vocabulary")
11749
+ vb_sub = verbs.add_subparsers(dest="verbs_cmd", required=True)
11750
+ vb_list = vb_sub.add_parser("list", help="List custom verbs")
11751
+ vb_list.add_argument("--key", default=config.api_key())
11752
+ vb_add = vb_sub.add_parser("add", help="Add a custom verb")
11753
+ vb_add.add_argument("name")
11754
+ vb_add.add_argument("--description", default="")
11755
+ vb_add.add_argument("--resolved-verb", default="", help="Canonical verb this aliases")
11756
+ vb_add.add_argument("--key", default=config.api_key())
11757
+ vb_rename = vb_sub.add_parser("rename", help="Rename or redescribe a custom verb")
11758
+ vb_rename.add_argument("name")
11759
+ vb_rename.add_argument("--new-name", default="")
11760
+ vb_rename.add_argument("--description", default="")
11761
+ vb_rename.add_argument("--key", default=config.api_key())
11762
+ vb_retire = vb_sub.add_parser("retire", help="Retire a custom verb")
11763
+ vb_retire.add_argument("name")
11764
+ vb_retire.add_argument("--key", default=config.api_key())
11765
+
11766
+ keys = sub.add_parser("keys", help="Manage tenant API keys")
11767
+ keys_sub = keys.add_subparsers(dest="keys_cmd", required=True)
11768
+ keys_list = keys_sub.add_parser("list", help="List key metadata")
11769
+ keys_list.add_argument("--key", default=config.api_key())
11770
+ keys_create = keys_sub.add_parser("create", help="Create a new API key (TOTP-gated)")
11771
+ keys_create.add_argument("--label", default="", help="Optional label")
11772
+ keys_create.add_argument("--principal-id", default="", help="Assign to a principal")
11773
+ keys_create.add_argument("--totp-code", default="", help="TOTP step-up code")
11774
+ keys_create.add_argument("--key", default=config.api_key())
11775
+ keys_revoke = keys_sub.add_parser("revoke", help="Revoke an API key by id")
11776
+ keys_revoke.add_argument("key_id")
11777
+ keys_revoke.add_argument("--key", default=config.api_key())
11778
+
11779
+ badge = sub.add_parser("badge", help="Dynamic README badge tokens")
11780
+ badge_sub = badge.add_subparsers(dest="badge_cmd", required=True)
11781
+ badge_create = badge_sub.add_parser("create", help="Create a public badge token")
11782
+ badge_create.add_argument("--label", default="gated")
11783
+ badge_create.add_argument("--key", default=config.api_key())
11784
+ badge_revoke = badge_sub.add_parser("revoke", help="Revoke a badge token")
11785
+ badge_revoke.add_argument("token")
11786
+ badge_revoke.add_argument("--key", default=config.api_key())
11787
+
11788
+ vault = sub.add_parser("vault", help="Credential vault")
11789
+ vault_sub = vault.add_subparsers(dest="vault_cmd", required=True)
11790
+ vault_list = vault_sub.add_parser("list", help="List enrolled credentials")
11791
+ vault_list.add_argument("--key", default=config.api_key())
11792
+
11793
+ stats = sub.add_parser("stats", help="24-hour receipt decision counters")
11794
+ stats.add_argument("--key", default=config.api_key())
11795
+
11796
+ lease = sub.add_parser("lease", help="Create a single-use gated-execution lease")
11797
+ lease.add_argument("--verb", required=True)
11798
+ lease.add_argument("--target", required=True)
11799
+ lease.add_argument("--purpose", required=True)
11800
+ lease.add_argument("--ttl", type=int, default=60, help="Lease TTL in seconds")
11801
+ lease.add_argument("--key", default=config.api_key())
11802
+
11803
+ execute = sub.add_parser("execute", help="Execute a gated upstream request using a lease")
11804
+ execute.add_argument("lease_id")
11805
+ execute.add_argument("--key", default=config.api_key())
11806
+
11721
11807
  args = parser.parse_args()
11722
11808
  if args.cmd == "key":
11723
11809
  payload = {"email": args.email}
@@ -11764,7 +11850,8 @@ def main():
11764
11850
 
11765
11851
  _KEY_COMMANDS = {"me", "policy", "check", "receipts", "stream",
11766
11852
  "verify-chain", "invite", "token-verify", "manifest-key",
11767
- "manifests", "github"}
11853
+ "manifests", "github", "principals", "verbs", "keys",
11854
+ "badge", "vault", "stats", "lease", "execute"}
11768
11855
  if args.cmd in _KEY_COMMANDS and not _require_key(args.key):
11769
11856
  return 2
11770
11857
 
@@ -11789,7 +11876,12 @@ def main():
11789
11876
  if args.cmd == "me":
11790
11877
  return _request_and_print("GET", "/v1/me", key=args.key)
11791
11878
  if args.cmd == "policy":
11792
- url = "/v1/policy" if not args.env else f"/v1/policies/environments/{args.env}"
11879
+ if args.principal:
11880
+ url = "/v1/policies/principals/" + urllib.parse.quote(args.principal, safe="")
11881
+ if args.env:
11882
+ url += "?environment=" + urllib.parse.quote(args.env, safe="")
11883
+ else:
11884
+ url = "/v1/policy" if not args.env else f"/v1/policies/environments/{args.env}"
11793
11885
  if args.set:
11794
11886
  for verb, mode in args.set:
11795
11887
  if mode not in MODES:
@@ -11798,6 +11890,8 @@ def main():
11798
11890
  return _request_and_print("PUT", url, {verb: mode for verb, mode in args.set}, key=args.key)
11799
11891
  return _request_and_print("GET", url, key=args.key)
11800
11892
  if args.cmd == "receipts":
11893
+ if args.receipt_id:
11894
+ return _request_and_print("GET", "/v1/receipts/" + urllib.parse.quote(args.receipt_id, safe=""), key=args.key)
11801
11895
  return _request_and_print("GET", "/v1/receipts", key=args.key)
11802
11896
  if args.cmd == "stream":
11803
11897
  return stream_cmd(args)
@@ -11849,6 +11943,72 @@ def main():
11849
11943
  if args.github_cmd == "disconnect":
11850
11944
  return _request_and_print("POST", "/v1/github/disconnect",
11851
11945
  {"installationId": args.installation_id, "totpCode": args.totp_code}, key=args.key)
11946
+ if args.cmd == "principals":
11947
+ if args.principals_cmd == "list":
11948
+ return _request_and_print("GET", "/v1/principals", key=args.key)
11949
+ if args.principals_cmd == "create":
11950
+ return _request_and_print("POST", "/v1/principals", {"name": args.name}, key=args.key)
11951
+ if args.principals_cmd == "rename":
11952
+ return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
11953
+ {"name": args.new_name}, key=args.key)
11954
+ if args.principals_cmd == "disable":
11955
+ return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
11956
+ {"disabled": True}, key=args.key)
11957
+ if args.principals_cmd == "enable":
11958
+ return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
11959
+ {"disabled": False}, key=args.key)
11960
+ if args.principals_cmd == "delete":
11961
+ qs = "?force=1" if args.force else ""
11962
+ return _request_and_print("DELETE", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe="") + qs, key=args.key)
11963
+ if args.principals_cmd == "revoke-keys":
11964
+ return _request_and_print("POST", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe="") + "/revoke",
11965
+ {}, key=args.key)
11966
+ if args.cmd == "verbs":
11967
+ if args.verbs_cmd == "list":
11968
+ return _request_and_print("GET", "/v1/verbs", key=args.key)
11969
+ if args.verbs_cmd == "add":
11970
+ body = {"name": args.name}
11971
+ if args.description:
11972
+ body["description"] = args.description
11973
+ if args.resolved_verb:
11974
+ body["resolvedVerb"] = args.resolved_verb
11975
+ return _request_and_print("POST", "/v1/verbs", body, key=args.key)
11976
+ if args.verbs_cmd == "rename":
11977
+ body = {}
11978
+ if args.new_name:
11979
+ body["name"] = args.new_name
11980
+ if args.description:
11981
+ body["description"] = args.description
11982
+ return _request_and_print("PATCH", "/v1/verbs/" + urllib.parse.quote(args.name, safe=""), body, key=args.key)
11983
+ if args.verbs_cmd == "retire":
11984
+ return _request_and_print("DELETE", "/v1/verbs/" + urllib.parse.quote(args.name, safe=""), key=args.key)
11985
+ if args.cmd == "keys":
11986
+ if args.keys_cmd == "list":
11987
+ return _request_and_print("GET", "/v1/keys", key=args.key)
11988
+ if args.keys_cmd == "create":
11989
+ body = {"totpCode": args.totp_code}
11990
+ if args.label:
11991
+ body["label"] = args.label
11992
+ if args.principal_id:
11993
+ body["principalId"] = args.principal_id
11994
+ return _request_and_print("POST", "/v1/keys", body, key=args.key)
11995
+ if args.keys_cmd == "revoke":
11996
+ return _request_and_print("DELETE", "/v1/keys/" + urllib.parse.quote(args.key_id, safe=""), key=args.key)
11997
+ if args.cmd == "badge":
11998
+ if args.badge_cmd == "create":
11999
+ return _request_and_print("POST", "/v1/badge/tokens", {"label": args.label}, key=args.key)
12000
+ if args.badge_cmd == "revoke":
12001
+ return _request_and_print("POST", "/v1/badge/tokens/revoke", {"token": args.token}, key=args.key)
12002
+ if args.cmd == "vault":
12003
+ if args.vault_cmd == "list":
12004
+ return _request_and_print("GET", "/v1/vault", key=args.key)
12005
+ if args.cmd == "stats":
12006
+ return _request_and_print("GET", "/v1/receipts/stats", key=args.key)
12007
+ if args.cmd == "lease":
12008
+ return _request_and_print("POST", "/v1/leases",
12009
+ {"verb": args.verb, "target": args.target, "purpose": args.purpose, "ttlSeconds": args.ttl}, key=args.key)
12010
+ if args.cmd == "execute":
12011
+ return _request_and_print("POST", "/v1/execute", {"leaseId": args.lease_id}, key=args.key)
11852
12012
  if args.cmd in ("revoke", "reinstate", "revoked"):
11853
12013
  if not _require_sponsor_token(args.sponsor_token):
11854
12014
  return 2
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes