OpenFief Give your AI agents a fief, not the kingdom.

Research notes

External facts the design and proposals draw on, as found on the date shown. This is evidence, not design: products, APIs and rules change, so re-verify before relying on any of it. Gathered with Claude (AI assistant), mostly through web search.

Products and prior art#

  • [R-1] (2026-10-25) No existing product combines portfolio tracking with budget-leased, policy-gated agent execution and per-role model routing. The closest open-source pieces:

  • [R-10] (2026-10-28) Wealthfolio’s web edition is one Docker image (wealthfolio/wealthfolio, also on GHCR) on port 8088, configured through WF_* environment variables, with SQLite on a volume and password or OIDC login; its HTTP endpoints live under /api/v1. Activities come in through CSV import in the UI, the paid Wealthfolio Connect broker sync, or its built-in MCP server, whose scoped, revocable tokens grant read access and, optionally, write scopes that draft and then commit activities. The docs describe no public REST API for writing activities.

  • [R-11] (2026-10-28) Ghostfolio’s MANUAL data source prices a custom asset by fetching the URL in its scraper configuration: a JSON response is read with a JSONPath expression, anything else as HTML with a CSS selector (apps/api/src/services/data-provider/manual/manual.service.ts at 9772282). Prior art: agusalex/ghostfolio-sync syncs IBKR Flex Queries into Ghostfolio, maps symbols in a mapping.yaml, and warns that data outside the Flex Query’s date window is lost if it was never synced. Ghostfolio’s API itself is R-15.

  • [R-12] (2026-10-27) OpenClaw plugins declare an openclaw.plugin.json manifest whose JSON Schema config is validated without running plugin code; the plugin itself is TypeScript loaded in-process and its docs say to treat plugins as trusted code. Its open skill registry, ClawHub, was hit by malware: Koi Security flagged 341 of 2,857 skills as malicious in late January 2026, and later counts ranged from about 900 to over 1,100.

  • [R-15] (2026-10-28) Ghostfolio’s API, read from its source (main at 9772282 and release 3.93.0, 2026-10-25):

    • Accounts: POST /api/v1/account takes name, currency, platformId (may be null), optional balance, comment, tags and a client-chosen id, which must be a UUID to be usable in filters. The isExcluded flag was removed in 3.39.0; a tag replaces it.
    • Activities: /api/v1/order was removed in 3.5.0. GET /api/v1/activities?accounts=<uuids> lists them with their comment, all at once when take is omitted. DELETE /api/v1/activities/:id deletes one; DELETE /api/v1/activities with filters deletes in bulk, and with no filter deletes everything. Deleting an asset’s last activity also deletes the asset and its market prices.
    • Import: POST /api/v1/import?dryRun=true validates without writing. The body holds activities (comment up to 2000 characters, no client-chosen ID, unknown fields rejected) and optionally accounts, assetProfiles, platforms and tags. Duplicates, matched on comment, currency, data source, date, fee, quantity, symbol, type and unit price, are skipped silently. An import isn’t rolled back on error. The body is capped at 10 MB.
    • Manual assets: a MANUAL symbol must be a UUID (a GF_ prefix is replaced by a random UUID). Prices are set through assetProfiles[].marketData in an import or POST /api/v1/market-data/MANUAL/:symbol, by a normal user for their own assets.
    • Comments are blanked in responses when the user’s “restricted view” setting is on.
    • Auth: POST /api/v1/auth/anonymous exchanges the security token for a bearer token valid for 180 days; rotating the security token doesn’t revoke issued ones.
    • Stability: the README’s public API covers only auth, health, import, export and public portfolio pages. Accounts, activities and market-data are the web app’s internal endpoints, renamed within /v1 without notice; there is no OpenAPI spec.
    • https://github.com/ghostfolio/ghostfolio (apps/api/src/app/endpoints/import, apps/api/src/app/activities, apps/api/src/services/import/import.service.ts)

Brokers and data sources#

Security#

  • [R-6] (2026-10-25) Prompt injection against agents that move money is documented in real incidents, such as the May 2026 Grok/Bankrbot wallet drain. There is research on adversarial news attacks against multi-agent trading systems: https://arxiv.org/pdf/2608.24069

  • [R-13] (2026-10-27) Hostname-based egress control for pods. Standard Kubernetes NetworkPolicy matches IP blocks, pods and namespaces, never hostnames. The upstream standard for domain names (AdminNetworkPolicy domainNames, NPEP-133) is still provisional. Domain policies exist in Cilium (toFQDNs), in GKE (FQDNNetworkPolicy, which cannot tell apart domains served by the same IP) and in Calico’s commercial editions. CNI-independent options: nais/fqdn-policy, which writes NetworkPolicy ipBlocks from DNS, and Stripe’s Smokescreen, a Go CONNECT proxy with per-client allow-lists and a replaceable client-identity function.

  • [R-14] (2026-10-28) Smokescreen, read from its source (master at 68e7b82, 2026-10-22):

    • MIT licensed; its vendored dependencies are BSD-3-Clause, MIT and Apache-2.0.
    • Maintained by Stripe: 57 commits in the past year, no open issues. Tags are sparse (v0.1.0 on 2026-09-18, the one before in 2022), and fixes since v0.1.0 include blocking connections to the proxy’s own addresses. It is v0, with no API stability promise.
    • Embeddable: package pkg/smokescreen exports NewConfig, StartWithConfig and BuildProxy. Config.RoleFromRequest receives the CONNECT request with its Proxy-Authorization header. Config.EgressACL takes a per-role allow-list, which can be built in code; it isn’t reloaded at runtime.
    • If EgressACL is nil, every destination is allowed. StartWithConfig exits the process on errors and handles SIGHUP, SIGTERM and SIGUSR2 itself.
    • It denies loopback, link-local (including cloud metadata addresses), private, CGNAT and IPv4-embedding IPv6 ranges by default, resolves hostnames itself, and logs one decision line per request with role, host and outcome.
    • Go’s net/http and undici’s ProxyAgent send user:password from the proxy URL as a Basic Proxy-Authorization header on CONNECT; Node’s built-in client reads HTTPS_PROXY only with NODE_USE_ENV_PROXY=1.
    • https://github.com/stripe/smokescreen

Source: docs/research.md · last changed 2026-10-28 in 01d051b