Skip to content

Package web search as a standalone Pi Pocket extension - #126

Closed
piromagnus wants to merge 3 commits into
TannerMidd:mainfrom
piromagnus:feat/web-access-search
Closed

piromagnus wants to merge 3 commits into
TannerMidd:mainfrom
piromagnus:feat/web-access-search

Conversation

@piromagnus

@piromagnus piromagnus commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Provides an installable web-search extension for Pi Pocket 0.20.0+, using the experimental host.fetchPublic API. It exposes owner-only settings through public extension routes and a menu/launcher sheet, with its own stored keys and data under host.dataDir.

Search supports configurable quick/standard/deep provider ordering, manual overrides and fallback confined to the selected tier. Provider options cover Exa search/content and Tavily search, extraction, crawl and research. The local ledger tracks observed requests, outcomes and reported credits separately.

External tool output is defanged and fenced. The README documents installation, every contacted hostname and transmitted data, key isolation, experimental API requirements and accounting limits. Research creation remains non-replay-safe.

Validation: TypeScript, ESLint, formatting and 22 tests pass, including installation, owner/non-owner routes, disable cleanup, gallery manifest validation and public-network guards against unmodified Pi Pocket 0.20. Tests use fake provider responses. Chromium could not launch in this environment, so browser validation remains pending; no paid provider calls were made.

Add the shared provider manager, validated settings, atomic request ledger, owner-only API and configuration sheet. Keep the built-in tool module in a separate commit under contribution policy C17.

Local review: trail check reported keyword matches against subscription-usage notes. Those refer to unrelated account credential and cache code; this patch has no subscription lookup or account-report cache. Web provider tests explicitly reject subscription tokens.
Register search, extraction, crawl, research creation and research status tools. Use only the host service at runtime; research submission is not replay-safe.

Local review: trail check matches against unrelated subscription usage notes do not apply. This module does not read subscription credentials or maintain account-report caches.
@piromagnus
piromagnus marked this pull request as ready for review October 22, 2026 17:15
TannerMidd added a commit that referenced this pull request Oct 22, 2026
A module that fetches an address from outside (a page Pi names, a search
result's link, a crawl) had only fetch, which reaches this machine, the
local network, and a cloud's metadata address. PR #126 had to put web
search in core to keep that check there (C4).

`host.fetchPublic(url, init)` resolves names where it connects and
refuses one with any address that is not public, so DNS rebinding gets
nowhere; IP literals are checked first. Each redirect is checked again;
a 303 becomes a GET without its body's headers, and another origin gets
no credentials. It stops at 10 MiB decoded, 30 s, and 5 redirects.
Refusals carry a code: POCKET_NOT_PUBLIC, POCKET_TOO_LARGE,
POCKET_REDIRECTS.

Experimental in 0.20.0. Its first user is docs/examples/read-page.ts, a
read_page tool that fences the page's text with host.text (C15). Docs:
host reference and guide, limits, API versions, security model, and
the web access case in core-or-extension.

Tested: test/public-fetch.test.ts (address ranges, literal forms,
names, redirects, limits, timeout), the examples test (read_page
refuses localhost), npm run check, lint, and npm test (font-ui is
flaky once, passes alone). A read-only review found no SSRF bypass;
its four correctness findings are fixed.
@TannerMidd

TannerMidd commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

This comment was written by an AI agent (Pi), and reviewed and directed by the repository owner.

Thanks for this, @piromagnus: it's a thorough piece of work, and the tiered routing and request accounting are nice.

The plan is for this to live as a standalone extension rather than in core, so owners who want web search can install it and others don't carry it. Under core-or-extension, search providers, their keys and settings are an outside-service integration (C6, C8, C12). The one piece that does belong in core is the check that stops a fetched URL from reaching this machine or its network (C4), so that is the part that went into core.

The upcoming 0.20.0 release adds host.fetchPublic(url, init) (experimental), in a0b1345 on release/0.20.0 (#125):

  • Only http/https, and only public addresses: no loopback, private, link-local, CGNAT, metadata (169.254.169.254), IPv4-mapped, etc.
  • Names are resolved and checked where the connection is made, and the connection goes only to an address that was checked, so DNS rebinding is closed (the gap your PR notes).
  • Each redirect is checked again; a cross-origin redirect drops Authorization/Cookie.
  • Limits: 10 MiB decoded body, 30 s, 5 redirects (all adjustable per call). Refusals throw with code POCKET_NOT_PUBLIC, POCKET_TOO_LARGE, or POCKET_REDIRECTS.
  • Docs: host reference → fetchPublic; a small working example: docs/examples/read-page.ts.

Everything else in this PR already maps onto the public extension API:

In the PR As an extension
WebAccessManager, settings, storage Your module's own files; keep data under host.dataDir (e.g. ~/.pi-pocket/web-search/) rather than ~/.pi/
Routes in http/api.ts web.route(...): the default access is owner-only, same as requireOwner
Web access sheet, options, CSS A browser module folder via web.client("web-search"), using pocket.sheet, pocket.menu, pocket.style
Tools Unchanged (defineExtension); keep replay as you have it, with research not replay-safe
Direct URL checks in extract/crawl host.fetchPublic
host.webAccess, app.ts/host.ts/reload.ts edits Not needed

The only thing you lose is the link inside the Providers sheet; a menu row or launcher action covers it.

If you'd like to share it, you can submit it to the gallery (pi-pocket-extensions, see publishing and gallery review). A few things the review will look for:

  • Fence fetched text with host.text.defang/quoted before Pi reads it (search excerpts, extracted pages, crawl and research output); the current tools pass it through as-is.
  • Name every host it contacts and what it sends (G12), and say that it reads auth.json/environment keys if you keep that fallback (G13), or drop it and use only keys the extension stores.
  • Say it uses an experimental member (fetchPublic), and set minPocket to 0.20.0 (G6, A6). Use a name that doesn't start with pocket- (G3).

This PR stays open for you to decide whether to close it in favour of the extension. If anything in the extension API turns out not to be enough, open an issue titled "Extension point: …".

Use public 0.20 extension APIs, extension-owned keys/data and host.fetchPublic. Retain tier routing and observed request accounting; fence external tool output. TypeScript, lint, formatting and 22 tests passed for this exact prepared package. Browser validation remains pending.

Scoped trail check reports keyword matches in negative security assertions and mocked tests, not semantic regressions: production has no unrestricted fetch or external credential fallback, no core Providers-sheet integration, and no cross-tier escalation. These boundaries are covered by tests; no branch overlaps were reported. Record that review in a new team learning.
@piromagnus piromagnus changed the title Add web access tools with tiered search and usage tracking Package web search as a standalone Pi Pocket extension Oct 23, 2026
@piromagnus

Copy link
Copy Markdown
Contributor Author

Closing in favour of the standalone extension, as reviewed: the gallery entry is TannerMidd/pi-pocket-extensions#23, with the code at piromagnus/pi-pocket-web-search (pinned commit ce4314b). The core part — host.fetchPublic — already landed in 0.20.0 (a0b1345). Thank you for the detailed review.

@piromagnus piromagnus closed this Oct 23, 2026
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