getstack 1.0.0__tar.gz → 1.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.
- {getstack-1.0.0 → getstack-1.1.0}/PKG-INFO +1 -1
- {getstack-1.0.0 → getstack-1.1.0}/pyproject.toml +1 -1
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/inbound_webhooks.py +8 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/partner_tenants.py +14 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/inbound_webhooks.py +11 -2
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/partner_tenants.py +14 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_parity.py +1 -0
- getstack-1.1.0/tests/test_partner_signing.py +36 -0
- {getstack-1.0.0 → getstack-1.1.0}/.gitignore +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/CLAUDE.md +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/LICENSE +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/README.md +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/scripts/verify_sign_claim_crosslang.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/__init__.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/__init__.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/agents.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/audit.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/authority.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/credentials.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/delivery_methods.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/detector_config.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/dropoffs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/evidence_packs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/identity.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/intents.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/llm.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/missions.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/notifications.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/partner_connect.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/passport_session.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/passports.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/pii_config.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/proxy.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/reviews.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/scan.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/security_events.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/services.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/skills.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/team.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_async/usage.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/_authority_request.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/agent_auth.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/agents.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/audit.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/auth.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/authority.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/browser_bootstrap.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/client.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/credentials.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/delivery_methods.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/detector_config.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/dropoffs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/errors.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/evidence_packs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/identity.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/intents.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/llm.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/missions.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/notifications.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/partner_connect.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/passport_header_verify.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/passport_session.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/passports.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/pii_config.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/proxy.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/py.typed +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/reviews.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/scan.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/security_events.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/services.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/sign_claim.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/skills.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/team.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/types.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/usage.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/src/getstack/verify_offline.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_authority.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_delivery_methods.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_dropoffs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_errors.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_evidence_packs.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_identity_requirements.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_missions.py +0 -0
- {getstack-1.0.0 → getstack-1.1.0}/tests/test_sign_claim.py +0 -0
|
@@ -32,6 +32,14 @@ class AsyncInboundWebhookService:
|
|
|
32
32
|
res = await self._client.get("/v1/operator/inbound-webhooks")
|
|
33
33
|
return list(res.get("webhooks", []))
|
|
34
34
|
|
|
35
|
+
async def set_provider_signing_secret(self, webhook_id: str, signing_secret: str, downstream_signing_secret: Optional[str] = None) -> dict[str, Any]:
|
|
36
|
+
"""Human/admin setup: generic 32-256 byte key, or AgentMail whsec_ key and optional downstream key. Keys are never returned."""
|
|
37
|
+
from urllib.parse import quote
|
|
38
|
+
return await self._client.put(
|
|
39
|
+
f"/v1/operator/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
40
|
+
json={"signing_secret": signing_secret, **({"downstream_signing_secret": downstream_signing_secret} if downstream_signing_secret is not None else {})},
|
|
41
|
+
)
|
|
42
|
+
|
|
35
43
|
async def get(self, webhook_id: str) -> dict[str, Any]:
|
|
36
44
|
return await self._client.get(f"/v1/operator/inbound-webhooks/{webhook_id}")
|
|
37
45
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from typing import Any, Literal, Optional
|
|
6
|
+
from urllib.parse import quote
|
|
6
7
|
|
|
7
8
|
from ..client import AsyncHttpClient
|
|
8
9
|
|
|
@@ -54,6 +55,19 @@ class AsyncPartnerTenantsService:
|
|
|
54
55
|
json=body,
|
|
55
56
|
)
|
|
56
57
|
|
|
58
|
+
async def set_inbound_webhook_signing_secret(
|
|
59
|
+
self, tenant_id: str, webhook_id: str, signing_secret: str,
|
|
60
|
+
downstream_signing_secret: Optional[str] = None,
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Configure signing with direct partner admin authentication; returns status only."""
|
|
63
|
+
body = {"signing_secret": signing_secret}
|
|
64
|
+
if downstream_signing_secret is not None:
|
|
65
|
+
body["downstream_signing_secret"] = downstream_signing_secret
|
|
66
|
+
return await self._client.put(
|
|
67
|
+
f"/v1/partner/tenants/{quote(tenant_id, safe='')}/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
68
|
+
json=body,
|
|
69
|
+
)
|
|
70
|
+
|
|
57
71
|
async def issue_passport(
|
|
58
72
|
self,
|
|
59
73
|
tenant_id: str,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Hosted inbound webhook surface (Phase 5.5).
|
|
2
2
|
|
|
3
3
|
Operator creates a webhook row + receives a STACK-hosted URL + HMAC
|
|
4
|
-
secret.
|
|
5
|
-
|
|
4
|
+
secret. Generic providers sign with this secret. AgentMail endpoints
|
|
5
|
+
require their provider signing secret through set_provider_signing_secret.
|
|
6
|
+
STACK runs the prompt-injection detector on inbound content
|
|
6
7
|
before forwarding to a downstream URL.
|
|
7
8
|
"""
|
|
8
9
|
|
|
@@ -47,6 +48,14 @@ class InboundWebhookService:
|
|
|
47
48
|
res = self._client.get("/v1/operator/inbound-webhooks")
|
|
48
49
|
return list(res.get("webhooks", []))
|
|
49
50
|
|
|
51
|
+
def set_provider_signing_secret(self, webhook_id: str, signing_secret: str, downstream_signing_secret: Optional[str] = None) -> dict[str, Any]:
|
|
52
|
+
"""Human/admin setup: generic 32-256 byte key, or AgentMail whsec_ key and optional downstream key. Keys are never returned."""
|
|
53
|
+
from urllib.parse import quote
|
|
54
|
+
return self._client.put(
|
|
55
|
+
f"/v1/operator/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
56
|
+
json={"signing_secret": signing_secret, **({"downstream_signing_secret": downstream_signing_secret} if downstream_signing_secret is not None else {})},
|
|
57
|
+
)
|
|
58
|
+
|
|
50
59
|
def get(self, webhook_id: str) -> dict[str, Any]:
|
|
51
60
|
"""Get a single inbound webhook by id. Never returns the plaintext secret."""
|
|
52
61
|
return self._client.get(f"/v1/operator/inbound-webhooks/{webhook_id}")
|
|
@@ -8,6 +8,7 @@ their customers via this service.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from typing import Any, Literal, Optional
|
|
11
|
+
from urllib.parse import quote
|
|
11
12
|
|
|
12
13
|
from .client import HttpClient
|
|
13
14
|
|
|
@@ -59,6 +60,19 @@ class PartnerTenantsService:
|
|
|
59
60
|
json=body,
|
|
60
61
|
)
|
|
61
62
|
|
|
63
|
+
def set_inbound_webhook_signing_secret(
|
|
64
|
+
self, tenant_id: str, webhook_id: str, signing_secret: str,
|
|
65
|
+
downstream_signing_secret: Optional[str] = None,
|
|
66
|
+
) -> dict[str, Any]:
|
|
67
|
+
"""Configure signing with direct partner admin authentication; returns status only."""
|
|
68
|
+
body = {"signing_secret": signing_secret}
|
|
69
|
+
if downstream_signing_secret is not None:
|
|
70
|
+
body["downstream_signing_secret"] = downstream_signing_secret
|
|
71
|
+
return self._client.put(
|
|
72
|
+
f"/v1/partner/tenants/{quote(tenant_id, safe='')}/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
73
|
+
json=body,
|
|
74
|
+
)
|
|
75
|
+
|
|
62
76
|
def issue_passport(
|
|
63
77
|
self,
|
|
64
78
|
tenant_id: str,
|
|
@@ -52,6 +52,7 @@ EXPECTED_SERVICES = [
|
|
|
52
52
|
# (service, method) pairs that must exist on both clients — the integration-
|
|
53
53
|
# critical surface, incl. the gaps GET-244 closed.
|
|
54
54
|
EXPECTED_METHODS = [
|
|
55
|
+
("inbound_webhooks", "set_provider_signing_secret"),
|
|
55
56
|
("services", "connect"),
|
|
56
57
|
("services", "connect_custom"),
|
|
57
58
|
("services", "grant_agent_access"),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Tenant signing setup sends fixture keys in the body, never the URL."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import AsyncMock, Mock
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
8
|
+
|
|
9
|
+
from getstack.partner_tenants import PartnerTenantsService
|
|
10
|
+
from getstack._async.partner_tenants import AsyncPartnerTenantsService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_sync_tenant_signing():
|
|
14
|
+
client = Mock()
|
|
15
|
+
client.put.return_value = {"configured": True}
|
|
16
|
+
result = PartnerTenantsService(client).set_inbound_webhook_signing_secret(
|
|
17
|
+
"tenant fixture", "wh/fixture", "synthetic-provider", "synthetic-downstream",
|
|
18
|
+
)
|
|
19
|
+
assert result == {"configured": True}
|
|
20
|
+
client.put.assert_called_once_with(
|
|
21
|
+
"/v1/partner/tenants/tenant%20fixture/inbound-webhooks/wh%2Ffixture/provider-signing-secret",
|
|
22
|
+
json={"signing_secret": "synthetic-provider", "downstream_signing_secret": "synthetic-downstream"},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_async_tenant_signing():
|
|
27
|
+
client = Mock()
|
|
28
|
+
client.put = AsyncMock(return_value={"configured": True})
|
|
29
|
+
result = asyncio.run(AsyncPartnerTenantsService(client).set_inbound_webhook_signing_secret(
|
|
30
|
+
"tenant fixture", "wh/fixture", "synthetic-provider",
|
|
31
|
+
))
|
|
32
|
+
assert result == {"configured": True}
|
|
33
|
+
client.put.assert_awaited_once_with(
|
|
34
|
+
"/v1/partner/tenants/tenant%20fixture/inbound-webhooks/wh%2Ffixture/provider-signing-secret",
|
|
35
|
+
json={"signing_secret": "synthetic-provider"},
|
|
36
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|