← back to Ventura Claw
Add 5 no-auth SMB / civic public-data connectors
a5630c32573351c8244e9e2cefc2555c6e7642c3 · 2026-05-06 17:12:55 -0700 · SteveStudio2
Federal:
sec — SEC EDGAR (10-K filings, financial data, ticker lookup)
usaspending — federal contracts + grants search by keyword/recipient/year
Local-state:
nws — National Weather Service (forecasts + active alerts by lat/lon)
lacity — LA City Open Data (Socrata) — business tax registry, permits,
crime, code violations; SoQL query interface
cadata — California Open Data (CKAN) — package search + datastore query
All zero-auth — pre-connected for everyone. Health probes verified live on
Mac dev + Kamatera prod.
(USPTO was attempted but TSDR + PEDS recently auth-walled their public
endpoints; dropped from this batch.)
Files touched
M server/connectors.jsonA server/connectors/cadata.jsM server/connectors/index.jsA server/connectors/lacity.jsA server/connectors/nws.jsA server/connectors/sec.jsA server/connectors/usaspending.js
Diff
commit a5630c32573351c8244e9e2cefc2555c6e7642c3
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 6 17:12:55 2026 -0700
Add 5 no-auth SMB / civic public-data connectors
Federal:
sec — SEC EDGAR (10-K filings, financial data, ticker lookup)
usaspending — federal contracts + grants search by keyword/recipient/year
Local-state:
nws — National Weather Service (forecasts + active alerts by lat/lon)
lacity — LA City Open Data (Socrata) — business tax registry, permits,
crime, code violations; SoQL query interface
cadata — California Open Data (CKAN) — package search + datastore query
All zero-auth — pre-connected for everyone. Health probes verified live on
Mac dev + Kamatera prod.
(USPTO was attempted but TSDR + PEDS recently auth-walled their public
endpoints; dropped from this batch.)
---
server/connectors.json | 60 ++++++++++++++++++++++++++++++++++++++
server/connectors/cadata.js | 63 ++++++++++++++++++++++++++++++++++++++++
server/connectors/index.js | 18 +++++++++++-
server/connectors/lacity.js | 55 +++++++++++++++++++++++++++++++++++
server/connectors/nws.js | 54 ++++++++++++++++++++++++++++++++++
server/connectors/sec.js | 58 ++++++++++++++++++++++++++++++++++++
server/connectors/usaspending.js | 46 +++++++++++++++++++++++++++++
7 files changed, 353 insertions(+), 1 deletion(-)
diff --git a/server/connectors.json b/server/connectors.json
index cd8a8e2..04bd93a 100644
--- a/server/connectors.json
+++ b/server/connectors.json
@@ -751,5 +751,65 @@
"logo": null,
"tint": "#d4a04a",
"sensitive": false
+ },
+ {
+ "id": "sec",
+ "name": "SEC EDGAR",
+ "category": "data",
+ "docs": "https://www.sec.gov/edgar/sec-api-documentation",
+ "auth": "none",
+ "triggers": 0,
+ "actions": 3,
+ "logo": null,
+ "tint": "#003366",
+ "sensitive": false
+ },
+ {
+ "id": "usaspending",
+ "name": "USAspending",
+ "category": "data",
+ "docs": "https://api.usaspending.gov/",
+ "auth": "none",
+ "triggers": 0,
+ "actions": 2,
+ "logo": null,
+ "tint": "#1B3A6B",
+ "sensitive": false
+ },
+ {
+ "id": "nws",
+ "name": "NWS Weather",
+ "category": "data",
+ "docs": "https://www.weather.gov/documentation/services-web-api",
+ "auth": "none",
+ "triggers": 0,
+ "actions": 2,
+ "logo": null,
+ "tint": "#0078D4",
+ "sensitive": false
+ },
+ {
+ "id": "lacity",
+ "name": "LA City Open Data",
+ "category": "data",
+ "docs": "https://data.lacity.org/browse",
+ "auth": "none",
+ "triggers": 0,
+ "actions": 3,
+ "logo": null,
+ "tint": "#FFC72C",
+ "sensitive": false
+ },
+ {
+ "id": "cadata",
+ "name": "California Open Data",
+ "category": "data",
+ "docs": "https://data.ca.gov/dataset",
+ "auth": "none",
+ "triggers": 0,
+ "actions": 2,
+ "logo": null,
+ "tint": "#F1B82D",
+ "sensitive": false
}
]
\ No newline at end of file
diff --git a/server/connectors/cadata.js b/server/connectors/cadata.js
new file mode 100644
index 0000000..ca5d068
--- /dev/null
+++ b/server/connectors/cadata.js
@@ -0,0 +1,63 @@
+// California Open Data (Socrata, hosted on data.ca.gov). No auth needed.
+// Catalog: https://data.ca.gov/dataset
+// Useful datasets:
+// - CA SOS Business Filings: 8ek2-eknp (SOS active business search)
+// - CalEnviroScreen 4.0: <varies>
+// - State Contracts Inventory: ng9z-2spp
+// - Active Civil Cases: c9b6-2v9a
+const BASE = "https://data.ca.gov/api/3/action/datastore_search";
+async function call(qs) {
+ const r = await fetch(`${BASE}?${qs}`, {
+ headers: { Accept: "application/json", "User-Agent": "VenturaClaw/1.0" },
+ signal: AbortSignal.timeout(20_000),
+ });
+ if (!r.ok) throw new Error(`cadata ${r.status}`);
+ return r.json();
+}
+async function packageList() {
+ const r = await fetch("https://data.ca.gov/api/3/action/package_list?limit=10", {
+ headers: { Accept: "application/json", "User-Agent": "VenturaClaw/1.0" },
+ signal: AbortSignal.timeout(15_000),
+ });
+ if (!r.ok) throw new Error(`cadata package_list ${r.status}`);
+ return r.json();
+}
+module.exports = {
+ meta: { id: "cadata", name: "California Open Data", category: "data", docsUrl: "https://data.ca.gov/dataset", auth: "none", realImpl: true },
+ fields: [],
+ configured() { return true; },
+ async health() {
+ try {
+ const d = await packageList();
+ return { ok: true, label: `live · CKAN datastore · ${(d.result || []).length}+ packages sampled` };
+ } catch (e) { return { ok: false, reason: e.message }; }
+ },
+ actions: {
+ async "search_packages"({ q, rows }) {
+ if (!q) throw new Error("q required");
+ const qs = new URLSearchParams({ q, rows: String(Math.min(rows || 20, 100)) });
+ const r = await fetch(`https://data.ca.gov/api/3/action/package_search?${qs}`, {
+ headers: { Accept: "application/json", "User-Agent": "VenturaClaw/1.0" },
+ signal: AbortSignal.timeout(15_000),
+ });
+ if (!r.ok) throw new Error(`cadata search ${r.status}`);
+ const d = await r.json();
+ return {
+ count: d.result?.count || 0,
+ packages: (d.result?.results || []).map(p => ({
+ id: p.id, name: p.name, title: p.title, organization: p.organization?.title,
+ notes: p.notes?.slice(0, 240),
+ resources: (p.resources || []).map(r => ({ id: r.id, name: r.name, format: r.format, url: r.url })).slice(0, 5),
+ })),
+ };
+ },
+ async "datastore_query"({ resource_id, q, limit, offset }) {
+ if (!resource_id) throw new Error("resource_id required");
+ const qs = new URLSearchParams({ resource_id, limit: String(Math.min(limit || 50, 500)) });
+ if (offset) qs.set("offset", String(offset));
+ if (q) qs.set("q", q);
+ const d = await call(qs.toString());
+ return { total: d.result?.total, records: d.result?.records || [] };
+ },
+ },
+};
diff --git a/server/connectors/index.js b/server/connectors/index.js
index 6c699e8..c4af468 100644
--- a/server/connectors/index.js
+++ b/server/connectors/index.js
@@ -21,8 +21,14 @@ const cleveland = require("./cleveland");
const aic = require("./aic");
const wikipedia = require("./wikipedia");
const colorapi = require("./colorapi");
+// SMB / civic — federal + state + local public-data APIs.
+const sec = require("./sec");
+const usaspending = require("./usaspending");
+const nws = require("./nws");
+const lacity = require("./lacity");
+const cadata = require("./cadata");
-const REAL = { slack, stripe, cloudflare, mailchimp, hubspot, notion, airtable, twilio, figma, canva, etsy, gmail, purelymail, archive, elevenlabs, shopify, met, cleveland, aic, wikipedia, colorapi };
+const REAL = { slack, stripe, cloudflare, mailchimp, hubspot, notion, airtable, twilio, figma, canva, etsy, gmail, purelymail, archive, elevenlabs, shopify, met, cleveland, aic, wikipedia, colorapi, sec, usaspending, nws, lacity, cadata };
// Phase 8 — central sensitivity registry. Source of truth for "this action mutates state."
// Anything in WRITE_ACTIONS[id] requires the approval gate. Anything NOT listed is treated as read-only.
@@ -50,6 +56,11 @@ const WRITE_ACTIONS = {
aic: new Set([]),
wikipedia: new Set([]),
colorapi: new Set([]),
+ sec: new Set([]),
+ usaspending:new Set([]),
+ nws: new Set([]),
+ lacity: new Set([]),
+ cadata: new Set([]),
};
// Read actions enumerated per connector — explicit allowlist for fail-safe behavior.
@@ -75,6 +86,11 @@ const READ_ACTIONS = {
aic: new Set(["search", "artwork"]),
wikipedia: new Set(["summary", "search", "onthisday"]),
colorapi: new Set(["identify", "scheme"]),
+ sec: new Set(["ticker_lookup", "submissions", "company_concept"]),
+ usaspending:new Set(["search_awards", "recipient_summary"]),
+ nws: new Set(["forecast", "alerts"]),
+ lacity: new Set(["datasets", "query", "business_search"]),
+ cadata: new Set(["search_packages", "datastore_query"]),
};
function isWrite(id, action) {
diff --git a/server/connectors/lacity.js b/server/connectors/lacity.js
new file mode 100644
index 0000000..f154ebe
--- /dev/null
+++ b/server/connectors/lacity.js
@@ -0,0 +1,55 @@
+// LA City Open Data (Socrata). Read-only, no auth required for ≤1000 req/hr.
+// Catalog: https://data.lacity.org/browse
+// Useful datasets:
+// - Active Business Tax Registration: 6rrh-rzua
+// - Crime Data 2020-Present: 2nrs-mtv8
+// - Building & Safety Permits: pi9x-tg5x
+// - Code Violations: 855j-wf32
+const BASE = "https://data.lacity.org/resource";
+async function call(dataset, qs) {
+ const url = `${BASE}/${dataset}.json` + (qs ? `?${qs}` : "");
+ const r = await fetch(url, {
+ headers: { Accept: "application/json", "User-Agent": "VenturaClaw/1.0", "X-App-Token": "" },
+ signal: AbortSignal.timeout(20_000),
+ });
+ if (!r.ok) throw new Error(`lacity ${r.status}`);
+ return r.json();
+}
+const KNOWN = {
+ business_tax_registry: "6rrh-rzua",
+ building_permits: "pi9x-tg5x",
+ crime_data: "2nrs-mtv8",
+ code_violations: "855j-wf32",
+};
+module.exports = {
+ meta: { id: "lacity", name: "LA City Open Data", category: "data", docsUrl: "https://data.lacity.org/browse", auth: "none", realImpl: true },
+ fields: [],
+ configured() { return true; },
+ async health() {
+ try {
+ const d = await call(KNOWN.business_tax_registry, "$limit=1");
+ return { ok: true, label: `live · ${Object.keys(KNOWN).length} curated datasets (Socrata SoQL)` };
+ } catch (e) { return { ok: false, reason: e.message }; }
+ },
+ actions: {
+ async "datasets"() { return { datasets: KNOWN }; },
+ async "query"({ dataset, where, select, order, limit, q }) {
+ const id = KNOWN[dataset] || dataset;
+ if (!id || !/^[a-z0-9-]+$/i.test(id)) throw new Error("dataset id required");
+ const qs = new URLSearchParams();
+ if (where) qs.set("$where", where);
+ if (select) qs.set("$select", select);
+ if (order) qs.set("$order", order);
+ if (q) qs.set("$q", q);
+ qs.set("$limit", String(Math.min(limit || 50, 1000)));
+ return { rows: await call(id, qs.toString()) };
+ },
+ async "business_search"({ name, address, limit }) {
+ const where = [];
+ if (name) where.push(`upper(business_name) like upper('%${String(name).replace(/'/g, "''")}%')`);
+ if (address) where.push(`upper(street_address) like upper('%${String(address).replace(/'/g, "''")}%')`);
+ const qs = new URLSearchParams({ $where: where.join(" AND ") || "1=1", $limit: String(Math.min(limit || 25, 200)) });
+ return { rows: await call(KNOWN.business_tax_registry, qs.toString()) };
+ },
+ },
+};
diff --git a/server/connectors/nws.js b/server/connectors/nws.js
new file mode 100644
index 0000000..4e3fa06
--- /dev/null
+++ b/server/connectors/nws.js
@@ -0,0 +1,54 @@
+// US National Weather Service — public, no key. UA required.
+// https://www.weather.gov/documentation/services-web-api
+const BASE = "https://api.weather.gov";
+const UA = "VenturaClaw/1.0 (steve@venturaclaw.com)";
+async function call(path) {
+ const r = await fetch(`${BASE}${path}`, {
+ headers: { "User-Agent": UA, Accept: "application/geo+json" },
+ signal: AbortSignal.timeout(15_000),
+ });
+ if (!r.ok) throw new Error(`nws ${r.status}`);
+ return r.json();
+}
+module.exports = {
+ meta: { id: "nws", name: "NWS Weather", category: "data", docsUrl: "https://www.weather.gov/documentation/services-web-api", auth: "none", realImpl: true },
+ fields: [],
+ configured() { return true; },
+ async health() {
+ try {
+ // Pull a known LA gridpoint (Steve's home territory)
+ const d = await call("/points/34.0522,-118.2437");
+ return { ok: true, label: `live · LA gridpoint office=${d.properties?.gridId || "?"}` };
+ } catch (e) { return { ok: false, reason: e.message }; }
+ },
+ actions: {
+ async "forecast"({ lat, lon, hourly }) {
+ if (lat == null || lon == null) throw new Error("lat + lon required");
+ const pts = await call(`/points/${lat},${lon}`);
+ const url = hourly ? pts.properties?.forecastHourly : pts.properties?.forecast;
+ if (!url) throw new Error("no forecast url for that location");
+ const f = await fetch(url, { headers: { "User-Agent": UA }, signal: AbortSignal.timeout(15_000) }).then(r => r.json());
+ return {
+ location: pts.properties?.relativeLocation?.properties,
+ periods: (f.properties?.periods || []).slice(0, hourly ? 24 : 7).map(p => ({
+ name: p.name, startTime: p.startTime, temperature: p.temperature, temperatureUnit: p.temperatureUnit,
+ windSpeed: p.windSpeed, windDirection: p.windDirection, shortForecast: p.shortForecast, detailedForecast: p.detailedForecast,
+ })),
+ };
+ },
+ async "alerts"({ area, point }) {
+ const qs = new URLSearchParams();
+ if (area) qs.set("area", area); // 2-letter state code
+ if (point) qs.set("point", point); // "lat,lon"
+ const d = await call(`/alerts/active?${qs}`);
+ return {
+ count: (d.features || []).length,
+ alerts: (d.features || []).slice(0, 25).map(f => ({
+ headline: f.properties?.headline, severity: f.properties?.severity,
+ urgency: f.properties?.urgency, event: f.properties?.event, areaDesc: f.properties?.areaDesc,
+ effective: f.properties?.effective, expires: f.properties?.expires,
+ })),
+ };
+ },
+ },
+};
diff --git a/server/connectors/sec.js b/server/connectors/sec.js
new file mode 100644
index 0000000..c6cf302
--- /dev/null
+++ b/server/connectors/sec.js
@@ -0,0 +1,58 @@
+// SEC EDGAR — public-company filings & financial data. No key.
+// https://www.sec.gov/edgar/sec-api-documentation
+// SEC requires a descriptive User-Agent on every request.
+const UA = "VenturaClaw/1.0 (steve@venturaclaw.com)";
+async function call(url) {
+ const r = await fetch(url, {
+ headers: { "User-Agent": UA, Accept: "application/json" },
+ signal: AbortSignal.timeout(20_000),
+ });
+ if (!r.ok) throw new Error(`sec ${r.status}`);
+ return r.json();
+}
+function pad10(cik) { return String(cik).replace(/^0+/, "").padStart(10, "0"); }
+module.exports = {
+ meta: { id: "sec", name: "SEC EDGAR", category: "data", docsUrl: "https://www.sec.gov/edgar/sec-api-documentation", auth: "none", realImpl: true },
+ fields: [],
+ configured() { return true; },
+ async health() {
+ try {
+ const r = await fetch("https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0000320193&type=10-K&dateb=&owner=include&count=1", {
+ headers: { "User-Agent": UA }, signal: AbortSignal.timeout(15_000),
+ });
+ return { ok: r.ok, label: r.ok ? "live · public-company filings" : `http ${r.status}` };
+ } catch (e) { return { ok: false, reason: e.message }; }
+ },
+ actions: {
+ // Look up a company by ticker, returns CIK + name
+ async "ticker_lookup"({ ticker }) {
+ if (!ticker) throw new Error("ticker required");
+ const d = await call("https://www.sec.gov/files/company_tickers.json");
+ const t = String(ticker).toUpperCase();
+ for (const v of Object.values(d || {})) {
+ if (v.ticker === t) return { cik: pad10(v.cik_str), ticker: v.ticker, name: v.title };
+ }
+ return { error: "not_found" };
+ },
+ async "submissions"({ cik }) {
+ if (!cik) throw new Error("cik required");
+ const d = await call(`https://data.sec.gov/submissions/CIK${pad10(cik)}.json`);
+ return {
+ cik: d.cik, name: d.name, sic: d.sic, sicDescription: d.sicDescription,
+ tickers: d.tickers, exchanges: d.exchanges,
+ recent_filings: (d.filings?.recent?.form || []).slice(0, 20).map((form, i) => ({
+ form,
+ filingDate: d.filings.recent.filingDate[i],
+ accessionNumber: d.filings.recent.accessionNumber[i],
+ primaryDocument: d.filings.recent.primaryDocument[i],
+ })),
+ };
+ },
+ async "company_concept"({ cik, taxonomy, tag }) {
+ if (!cik || !tag) throw new Error("cik + tag required");
+ const t = taxonomy || "us-gaap";
+ const d = await call(`https://data.sec.gov/api/xbrl/companyconcept/CIK${pad10(cik)}/${t}/${tag}.json`);
+ return d;
+ },
+ },
+};
diff --git a/server/connectors/usaspending.js b/server/connectors/usaspending.js
new file mode 100644
index 0000000..e472583
--- /dev/null
+++ b/server/connectors/usaspending.js
@@ -0,0 +1,46 @@
+// USAspending.gov — federal awards / contracts / grants. No key.
+// https://api.usaspending.gov/
+const BASE = "https://api.usaspending.gov/api/v2";
+async function call(method, path, body) {
+ const r = await fetch(`${BASE}${path}`, {
+ method, headers: { "Content-Type": "application/json", Accept: "application/json", "User-Agent": "VenturaClaw/1.0" },
+ body: body ? JSON.stringify(body) : undefined,
+ signal: AbortSignal.timeout(25_000),
+ });
+ if (!r.ok) throw new Error(`usaspending ${r.status}: ${await r.text().then(t => t.slice(0, 180)).catch(()=>"")}`);
+ return r.json();
+}
+module.exports = {
+ meta: { id: "usaspending", name: "USAspending", category: "data", docsUrl: "https://api.usaspending.gov/", auth: "none", realImpl: true },
+ fields: [],
+ configured() { return true; },
+ async health() {
+ // Use a small, known-stable POST endpoint that doesn't require complex filters
+ try {
+ const d = await call("POST", "/search/spending_by_award_count/", {
+ filters: { award_type_codes: ["A", "B", "C", "D"] },
+ });
+ return { ok: true, label: "live · federal awards/contracts/grants data" };
+ } catch (e) { return { ok: false, reason: e.message }; }
+ },
+ actions: {
+ async "search_awards"({ keyword, recipient, award_type, fiscal_year, limit }) {
+ const filters = {};
+ if (keyword) filters.keywords = [keyword];
+ if (recipient) filters.recipient_search_text = [recipient];
+ if (award_type) filters.award_type_codes = Array.isArray(award_type) ? award_type : [award_type];
+ if (fiscal_year) filters.time_period = [{ start_date: `${fiscal_year}-10-01`, end_date: `${fiscal_year + 1}-09-30` }];
+ const d = await call("POST", "/search/spending_by_award/", {
+ filters,
+ fields: ["Award ID", "Recipient Name", "Award Amount", "Award Type", "Awarding Agency", "Description", "Start Date", "End Date"],
+ page: 1, limit: Math.min(limit || 25, 100),
+ });
+ return { total: d.results?.length || 0, awards: d.results || [] };
+ },
+ async "recipient_summary"({ duns_or_uei }) {
+ if (!duns_or_uei) throw new Error("duns_or_uei required");
+ const d = await call("GET", `/recipient/duns/${encodeURIComponent(duns_or_uei)}/`);
+ return d;
+ },
+ },
+};
← 4cd2509 Add 5 no-auth public-data connectors: MET, Cleveland, AIC, W
·
back to Ventura Claw
·
/services hero: tighter measurable hook — '47-second domain 5a1709c →