8000 feat(automations): call a web address, with credentials sealed in headers by adeelahmad · Pull Request #439 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(automations): call a web address, with credentials sealed in headers - #439

Merged
adeelahmad merged 1 commit into
mainfrom
claude/project-thread-mwk71t
Oct 15, 2026
Merged

adeelahmad merged 1 commit into
mainfrom
claude/project-thread-mwk71t

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Description

Before: an automation could message people and use the model, but it had no way to call a web service or an API.

After: the http_request action (workflow studio M7, Web group). It sends GET, POST or PUT to a url, with headers and a body. The body can be text, or an object, which is sent as JSON. It passes on {status, ok, body, json, content_type, truncated, method, url, headers}.

  • Reach: it calls public addresses only, or networks an admin allowed for notifications (notifications.networks). It connects to the address it checked (notify.request, pinned) and follows no redirect; a 3xx is passed on as it is.
  • Limits: a 10 second timeout, a 64 KB body, and the first 64 KB of the answer. The answer is outside text (untrusted: true), so a later prompt reads it marked as data.
  • Asking first: effect out, so it asks first unless the person set its node to run on its own.
  • Ledger: each call goes in the activity ledger as automation.http, with the method, host and status.
  • Credentials:
    • A header whose name says it carries a credential (Authorization, cookies, keys, tokens, secrets, passwords, signatures) is never shown; it reads ••••.
    • actions_web.seal_headers(db, cfg, space, automation, url, headers) seals those values with the namespace's key, bound to the automation id and to the address's scheme, host and port. A sealed value can't be used by another automation, another host or another namespace.
    • A locked vault stops the request.

notify.post now goes through the new notify.request(cfg, method, url, body, headers, …), which returns the status, content type and a capped body. Notifications behave as before.

Motivation and Context

Workflow studio, module M7 (plan: /mnt/project-files/workflow-studio/plan.md §5 M7), PR 3 of 5. #433 added notify, #438 added the AI actions, and Lens work and asset actions follow.

Choices taken (reversible):

  • Networks: it reuses the notification networks instead of adding a new setting.
  • Methods: no PATCH or DELETE yet, because DELETE would count as deleting.
  • Sealing: sealed headers are bound to the automation and the origin. M6 calls seal_headers when it saves an automation.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (ruff, ruff format, mypy clean locally).
  • I added or updated tests. tests/api/test_actions_web.py covers:
    • risk and params
    • GET, POST and PUT against a local server
    • the 64 KB cap, no redirects and the timeout
    • public addresses only, and refused params
    • sealed headers: masked, sent, and refused for another automation, host or namespace
  • My change requires documentation updates, and I have updated docs/ accordingly (docs/automations.md "Calling a web address" and the Actions row, docs/notifications.md networks).
  • Users need more than the title: n/a, people reach it once the engine (M6) runs automations.

Done questions (AGENTS.md)

  • Goal: the Web group of M7; merged on green CI.
  • Agents: in actions.catalog(), which M6 serves to the API, MCP and the assistant.
  • Zero setup: public addresses work at once; private ones use the existing notification networks setting.
  • Fewest clicks: method GET and no headers by default.
  • Configurable: n/a, no new setting; it reuses Settings → Notifications networks.
  • Light: standard library only, and no new dependency.
  • Private and secure: it asks first, reaches public addresses only, follows no redirects, and keeps credentials sealed and never shown. The threat model is extended for outbound HTTP (P-SSRF-GUARD, P-SECRETS, the redirects row) in md, yaml and json.
  • Cost: no model call; each request is in the activity ledger.
  • User decides: outbound always asks unless the person sets the node to run on its own.
  • Upgrades: no change to stored data.
  • Honest: docs label it built.
  • Tests and docs updated; no API change, so no OpenAPI regeneration.
  • Open standards: plain HTTP and JSON.
  • Refine later is written in docs/automations.md:
    • secrets in URLs sealed like headers
    • PATCH and DELETE
    • OAuth
    • form bodies
    • retries
    • separate networks for automations

🤖 Generated with Claude Code

https://claude.ai/code/session_01CyjR2zEfmTXBewKAx9Taw5


Generated by Claude Code

…ders

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CyjR2zEfmTXBewKAx9Taw5
@adeelahmad
adeelahmad merged commit cb49d8a into main Oct 15, 2026
11 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-mwk71t branch October 15, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0