getstack 1.1.0__tar.gz → 1.2.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.1.0 → getstack-1.2.0}/PKG-INFO +1 -1
- {getstack-1.1.0 → getstack-1.2.0}/pyproject.toml +1 -1
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/inbound_webhooks.py +1 -1
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/inbound_webhooks.py +7 -6
- getstack-1.2.0/tests/test_inbound_webhooks.py +51 -0
- {getstack-1.1.0 → getstack-1.2.0}/.gitignore +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/CLAUDE.md +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/LICENSE +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/README.md +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/scripts/verify_sign_claim_crosslang.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/__init__.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/__init__.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/agents.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/audit.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/authority.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/credentials.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/delivery_methods.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/detector_config.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/dropoffs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/evidence_packs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/identity.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/intents.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/llm.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/missions.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/notifications.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/partner_connect.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/partner_tenants.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/passport_session.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/passports.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/pii_config.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/proxy.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/reviews.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/scan.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/security_events.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/services.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/skills.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/team.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_async/usage.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/_authority_request.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/agent_auth.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/agents.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/audit.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/auth.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/authority.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/browser_bootstrap.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/client.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/credentials.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/delivery_methods.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/detector_config.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/dropoffs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/errors.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/evidence_packs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/identity.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/intents.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/llm.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/missions.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/notifications.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/partner_connect.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/partner_tenants.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/passport_header_verify.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/passport_session.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/passports.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/pii_config.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/proxy.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/py.typed +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/reviews.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/scan.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/security_events.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/services.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/sign_claim.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/skills.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/team.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/types.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/usage.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/src/getstack/verify_offline.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_authority.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_delivery_methods.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_dropoffs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_errors.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_evidence_packs.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_identity_requirements.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_missions.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_parity.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_partner_signing.py +0 -0
- {getstack-1.1.0 → getstack-1.2.0}/tests/test_sign_claim.py +0 -0
|
@@ -33,7 +33,7 @@ class AsyncInboundWebhookService:
|
|
|
33
33
|
return list(res.get("webhooks", []))
|
|
34
34
|
|
|
35
35
|
async def set_provider_signing_secret(self, webhook_id: str, signing_secret: str, downstream_signing_secret: Optional[str] = None) -> dict[str, Any]:
|
|
36
|
-
"""
|
|
36
|
+
"""Configure the incoming provider key. If a generic downstream key is omitted, STACK generates and returns its replacement once."""
|
|
37
37
|
from urllib.parse import quote
|
|
38
38
|
return await self._client.put(
|
|
39
39
|
f"/v1/operator/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Hosted inbound webhook surface (Phase 5.5).
|
|
2
2
|
|
|
3
|
-
Operator creates a webhook row + receives a STACK-hosted URL
|
|
4
|
-
|
|
3
|
+
Operator creates a webhook row + receives a STACK-hosted URL and distinct
|
|
4
|
+
incoming/downstream HMAC secrets. Generic providers sign with the incoming secret. AgentMail endpoints
|
|
5
5
|
require their provider signing secret through set_provider_signing_secret.
|
|
6
6
|
STACK runs the prompt-injection detector on inbound content
|
|
7
7
|
before forwarding to a downstream URL.
|
|
@@ -32,9 +32,10 @@ class InboundWebhookService:
|
|
|
32
32
|
"""Create a hosted inbound webhook.
|
|
33
33
|
|
|
34
34
|
Returns a dict with keys: id, provider, slug, webhook_url,
|
|
35
|
-
webhook_secret (
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
webhook_secret (downstream verification key, returned ONLY at create),
|
|
36
|
+
incoming_signing_secret (generic only, returned ONLY at create), agent_id,
|
|
37
|
+
forward_url, created_at. Capture both immediately. STACK never returns
|
|
38
|
+
them through list/get. If lost, revoke + recreate.
|
|
38
39
|
"""
|
|
39
40
|
body: dict[str, Any] = {"provider": provider, "agent_id": agent_id}
|
|
40
41
|
if forward_url is not None:
|
|
@@ -49,7 +50,7 @@ class InboundWebhookService:
|
|
|
49
50
|
return list(res.get("webhooks", []))
|
|
50
51
|
|
|
51
52
|
def set_provider_signing_secret(self, webhook_id: str, signing_secret: str, downstream_signing_secret: Optional[str] = None) -> dict[str, Any]:
|
|
52
|
-
"""
|
|
53
|
+
"""Configure the incoming provider key. If a generic downstream key is omitted, STACK generates and returns its replacement once."""
|
|
53
54
|
from urllib.parse import quote
|
|
54
55
|
return self._client.put(
|
|
55
56
|
f"/v1/operator/inbound-webhooks/{quote(webhook_id, safe='')}/provider-signing-secret",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Inbound webhook directional-key wire contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
10
|
+
|
|
11
|
+
from getstack._async.inbound_webhooks import AsyncInboundWebhookService # noqa: E402
|
|
12
|
+
from getstack.inbound_webhooks import InboundWebhookService # noqa: E402
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CaptureClient:
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
self.path: str | None = None
|
|
18
|
+
self.body: dict | None = None
|
|
19
|
+
|
|
20
|
+
def put(self, path: str, json: dict | None = None):
|
|
21
|
+
self.path = path
|
|
22
|
+
self.body = json
|
|
23
|
+
return {"configured": True, "webhook_secret": "synthetic-generated-downstream"}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AsyncCaptureClient(CaptureClient):
|
|
27
|
+
async def put(self, path: str, json: dict | None = None):
|
|
28
|
+
return super().put(path, json=json)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_sync_signing_setup_keeps_directional_keys_in_the_body() -> None:
|
|
32
|
+
client = CaptureClient()
|
|
33
|
+
result = InboundWebhookService(client).set_provider_signing_secret( # type: ignore[arg-type]
|
|
34
|
+
"iwh fixture", "synthetic-incoming", "synthetic-downstream"
|
|
35
|
+
)
|
|
36
|
+
assert client.path == "/v1/operator/inbound-webhooks/iwh%20fixture/provider-signing-secret"
|
|
37
|
+
assert client.body == {
|
|
38
|
+
"signing_secret": "synthetic-incoming",
|
|
39
|
+
"downstream_signing_secret": "synthetic-downstream",
|
|
40
|
+
}
|
|
41
|
+
assert result == {"configured": True, "webhook_secret": "synthetic-generated-downstream"}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_async_signing_setup_matches_sync() -> None:
|
|
45
|
+
client = AsyncCaptureClient()
|
|
46
|
+
result = asyncio.run(AsyncInboundWebhookService(client).set_provider_signing_secret( # type: ignore[arg-type]
|
|
47
|
+
"iwh fixture", "synthetic-incoming"
|
|
48
|
+
))
|
|
49
|
+
assert client.path == "/v1/operator/inbound-webhooks/iwh%20fixture/provider-signing-secret"
|
|
50
|
+
assert client.body == {"signing_secret": "synthetic-incoming"}
|
|
51
|
+
assert result == {"configured": True, "webhook_secret": "synthetic-generated-downstream"}
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|