Broker plugins#
SkyPortal integrates with external alert brokers (e.g. BOOM, Fink,
Lasair) through a pluggable provider interface, modeled on the follow-up
facility APIs (skyportal/facility_apis/). A broker provider is a registered
class; a configured connection to a broker is a Broker database record that
supplies the per-instance endpoints/credentials the provider operates on.
This lets a new broker be added as a provider class (and shared helpers) instead of forking SkyPortal or re-deriving the same integration in every deployment.
Concepts#
BrokerAPI(skyportal/broker_apis/interface.py), the base class. Every operation is a stub that raisesNotImplementedError; a provider overrides only what it supports.Registry, providers are listed in the
BROKERStuple inskyportal/broker_apis/__init__.py. Thebroker_classnamesPostgres enum (skyportal/enum_types.py) is derived from it, so provider names are validated at the database level (mirrorsapi_classnames). Append new providers to the end of the tuple to keep the enum stable.Capabilities,
BrokerAPI.implements()reports which operations a provider overrode. Handlers gate on it, and the frontend can show/hide features accordingly.Brokermodel (skyportal/models/broker.py), one configured broker:name,broker_classname(which provider),active,ingest, and encryptedaltdata(endpoints/credentials, mirroringAllocation.altdata). Only system admins may create/update/delete brokers, andaltdatais redacted from non-admins.activemakes the connection usable on demand;ingestis what subscribes the instance to its stream, and the two are set separately because a broker worth searching by hand is not necessarily one to consume in full.Site defaults,
default_alert_search,default_crossmatchanddefault_photometryname the broker the source page’s “Search alerts” button opens, the one its cross-matches (cone searches) run against, and the one serving the photometry the source page displays (its saved points merged with photometry pulled from the broker on demand, cached on disk formisc.minutes_to_keep_broker_photometry_cacheand never written to the database). At most one broker holds each; pick them on the brokers page. The seeded data makes ALeRCE the alert search and cross-match one; no broker serves photometry until one is picked. The fetched points are scoped to the requester exactly like the alert search: a(survey, programid)group is only displayed to users whose streams cover that programid, so ZTF partnership and Caltech points stay hidden from a public-only user.PhotStatis still recomputed over everything fetched, matching how it is already computed over every saved row regardless of who is looking. Making an active broker a default re-runs itstest_connection, so an unreachable one is refused; a broker that goes down later never breaks a source page, the lightcurve falls back to the saved photometry, the failure is logged, and a broker that times out is skipped for a minute so an outage does not make every source page wait for one. An object the broker simply does not know (a 404) is cached as empty and leaves the passthrough on for every other object.
Operations#
Interactive (SkyPortal → broker): query_alerts, get_alert, get_cutouts,
cone_search, save_as_source, get_photometry, and filter management
(get_filters, create_filter, update_filter, delete_filter,
test_filter, filter_modules).
save_as_source and get_photometry come free with get_alert, but the
photometry passthrough runs on every source page view under a 10s bound, so a
provider whose object fetch cannot meet that sets photometry_passthrough = False and stops advertising the capability. ANTARES (walks its whole paginated
alert history) and Pitt-Google (bills the deployment for a BigQuery job per view)
opt out. Lasair advertises it, but its API is throttled per token per hour (100
calls for a user token, 10,000 for a power user), so only pick it as the
photometry default on a power-user token.
Ingestion (broker → SkyPortal): run_ingestion, a long-lived consumer/poller
(see “Ingestion and filters” below).
Ingestion and filters (end to end)#
Once a broker is configured (a Broker record with valid altdata), ingestion,
running its filters and pulling in matching alerts, is driven by a background
service and gated by config.
1. Enable the ingestion service#
The broker_ingest service (services/broker_ingest/) runs one task per broker
that is active, has ingest set, and whose provider implements run_ingestion. It is off by default; enable
it in your config and restart the app:
brokers:
ingest_enabled: true
With it disabled the service idles (it does not exit), so nothing is ever polled.
Confirm it via make monitor (the broker_ingest entry should be RUNNING) and
log/broker_ingest.log, which prints either broker ingestion disabled ... or
starting ingestion for broker <id> (<name>).
brokers.ingest_enabled is a config value, not in the database, not in a
broker’s altdata, and not exposed via the API. A broker’s run_ingestion: true
capability only means the provider supports ingestion, not that it is running.
2. Attach filters#
A filter_kind: "query" broker (e.g. Lasair) runs one query per SkyPortal Filter
linked to it. Create a Filter (on a Stream + your Group), then attach the
query, for Lasair the SELECT / FROM / WHERE parts, from the broker page (or
POST /api/brokers/<id>/filters/<filter_id>). Filters are re-read every poll
cycle, so adding/editing one does not require a restart.
3. When and how often#
run_ingestion polls immediately on start, then sleeps altdata.poll_interval
seconds (provider default; e.g. Lasair 86400 = daily, ALeRCE/ANTARES 3600), so the
first batch appears within seconds of the service starting, not after a full
interval. Changing poll_interval (or other broker altdata) is read once when a
broker’s task starts, so restart the ingestion service to apply it.
4. Where results land#
Matching objects are registered as Candidates under the passing filter and appear on the Candidates / Scanning page filtered by it, not as Sources. From there you review and save the ones worth keeping.
5. Auto-save#
To save passing objects as Sources automatically (into the filter’s group)
instead of only registering candidates, set the filter’s autosave flag, the
“Auto-save passing objects as sources” checkbox in the Lasair filter builder, or
POST /api/brokers/<id>/filters/<filter_id> with {"autosave": true}. Objects
already saved to that group are skipped. Use it only when the filter is selective
enough to trust everything it passes; otherwise keep it off and tighten the
filter’s conditions so scanning stays manageable.
Endpoints#
GET/POST/PATCH/DELETE /api/brokers[/{id}], manageBrokerrecords.GET /api/brokers/{id}/alerts[/{alert_id}], query alerts (dispatched to the broker’s provider).GET /api/internal/broker_apis, capabilities + config schema of every registered provider (for the frontend).
Writing a provider#
Add
skyportal/broker_apis/mybroker.pywith aMYBROKER(BrokerAPI)class overriding the operations you support. Read per-instance config frombroker.altdata. Provideform_json_schema_config(and optionallyui_json_schema,surveys,validate_config).Append it to
BROKERSinskyportal/broker_apis/__init__.py.A database migration for the extended
broker_apisenum is generated automatically.
See skyportal/broker_apis/generic.py (GENERICBROKER) for a working reference
that talks to any REST broker via a configured base_url/token.