Signet
An autonomous fire intelligence agent that continuously monitors the continental United States for wildfire activity using satellite imagery and geospatial data.
Watch an AI reason about wildfires in real-time →
| Live Reasoning Feed | Prediction Tracking | Autonomous Decision |
|---|---|---|
![]() |
![]() |
![]() |
| Real-time stream of Gemini's thought process, tool calls, and decisions during an autonomous cycle | Time-bound predictions with confidence levels and verification status | decide_live_cycle output showing Gemini choosing what to run next and why update_alert_criteria tool call showing Gemini adjusting its own detection thresholds and monitoring priorities based on evolving conditions |
Overview
Signet picks up a VIIRS thermal detection in Northern California - 12.4 MW of radiative power at 39.2°N, 121.8°W. It pulls the GOES-19 fire temperature image for the Pacific Southwest sector and confirms active hot pixels. It checks NWS: 14% humidity, 35 km/h northeast winds, Haines Index 6. Fuel model: SH7, very high load dry climate shrub. 2,400 people within 10 km. 847 structures.
It creates an incident, rates it critical, and predicts southwest spread within 6 hours based on terrain channeling and wind direction. Next cycle, it verifies that prediction against new satellite data.
Nobody asked it to do any of this. It decided on its own.
VIIRS (Visible Infrared Imaging Radiometer Suite) instruments alone registered over 21 million fire detections globally in 2023 (Dhage, 2025). The bottleneck has never been detection - it's interpretation. A thermal hotspot is meaningless without knowing the wind, the fuel, the terrain, and what's downhill. Today that synthesis happens manually, during business hours, one fire at a time.
Signet replaces that workflow with a single autonomous loop. Gemini 3 fuses satellite imagery with geospatial data, makes predictions, verifies them against subsequent observations, and adjusts its own monitoring strategy - continuously, for hours and days, without human intervention.
Satellites tell you where the heat is. Signet tells you what it means.
Architecture

The Autonomous Loop
Each cycle, Gemini 3 executes the following without human intervention:
- Restore state -
load_cycle_stateretrieves priorities, hypotheses, and learnings from the previous cycle - Check predictions -
get_due_predictionsfinds predictions that have reached their verification window - Process intelligence - Analyzes new FIRMS detections or GOES imagery with geospatial enrichment
- Act - Creates/updates incidents, generates risk zones, produces threat assessments
- Predict - Makes new time-bound predictions about fire behavior
- Learn - Resolves predictions against outcomes, adjusts thresholds if accuracy drops
- Decide next -
decide_live_cyclechooses what to run next and when, closing the autonomous loop
Cycle Types
The system runs four cycle types, all triggered and scheduled by Gemini itself via decide_live_cycle:
- FIRMS Cycle - New NASA FIRMS thermal detections arrive. Gemini filters by adaptive thresholds it has tuned itself, deduplicates, correlates against GOES observations, then enriches each detection with weather, fuel, terrain, population, and structures before deciding whether to create an incident, escalate an existing one, or dismiss it.
- GOES Cycle - GOES-19 Fire Temperature RGB imagery is sent directly to Gemini as image content. The model interprets thermal patterns in the imagery - distinguishing active fire pixels from surface heating artifacts - and cross-references against FIRMS detections and tracked incidents.
- Investigation Cycle - Self-initiated by the agent. When Gemini identifies an area of emerging risk based on weather patterns, prediction outcomes, or GOES anomalies, it autonomously requests a deep investigation of specific coordinates - pulling every available data source to build a comprehensive risk assessment for a location nobody told it to look at.
- Risk Outlook - Periodic risk outlook synthesizing all active incidents, risk zones, detections, and geospatial conditions into a forward-looking assessment with a 12–24 hour horizon.
Gemini 3 Integration
Signet is built on Gemini 3. These are the capabilities that make continuous autonomous monitoring possible:
Thought Signatures - Reasoning Continuity Across Tool Chains
// In the streaming loop, thought signatures are explicitly preserved
// across every multi-turn tool call exchange
var validParts []*genai.Part
for _, p := range allParts {
if p.FunctionCall != nil || p.Text != "" || p.FunctionResponse != nil ||
p.InlineData != nil || len(p.ThoughtSignature) > 0 {
validParts = append(validParts, p)
}
}
contents = append(contents, &genai.Content{
Role: "model",
Parts: validParts,
})
A single FIRMS cycle can involve 15+ sequential tool calls. Between each call, the model needs to remember what it was reasoning about - why it was suspicious of a detection, what the weather implied, what it planned to check next. Thought signatures are opaque tokens returned by Gemini that encode the model's reasoning state. By filtering and preserving them in every model turn during the streaming loop, Signet maintains cognitive continuity across the full multi-step tool chain without re-summarizing context.
This is the critical difference between an agent that makes sequential independent calls and one that thinks through a chain of evidence. When Signet checks weather after seeing a GOES thermal anomaly, the model's assessment of the imagery is still live in its reasoning state - it doesn't re-derive its suspicion from the text history.
Thinking Levels - Variable Reasoning Depth
// Full analysis cycles: medium thinking for complex multi-source reasoning
gc.RunWithOptions(ctx, prompt, images, gemini.Execute, observer, &gemini.RunOptions{
Stream: true,
ThinkingLevel: genai.ThinkingLevelMedium,
})
// Scheduling decisions: low thinking for fast operational choices
gc.RunWithOptions(ctx, prompt, nil, gemini.Execute, observer, &gemini.RunOptions{
ToolNames: []string{"decide_live_cycle", "get_alert_criteria",
"list_risk_zones", "get_due_predictions"},
ThinkingLevel: genai.ThinkingLevelLow,
})
Not every decision requires the same depth. FIRMS enrichment, GOES imagery analysis, and proactive investigations use medium thinking - the model needs to weigh conflicting signals across weather, terrain, fuel load, and satellite imagery to distinguish a real wildfire from agricultural burning or industrial heat. Scheduling decisions via decide_live_cycle and grounding queries use low thinking - speed matters more than nuance for operational routing. The system dynamically selects thinking level per operation type.
Real-Time Cognitive Streaming
if event.Type == "thought_delta" || event.Type == "text_delta" {
apiServer.PublishEvent("stream_delta", map[string]any{
"type": event.Type,
"content": event.Content,
"cycle_id": cycleID,
})
}
IncludeThoughts: true combined with Gemini's streaming API means the model's internal reasoning is visible as it happens. Every thought delta and text delta is pushed through server-sent events to the browser, where the frontend renders a live indicator showing whether the model is "Thinking..." or "Responding..." and which cycle type is running. This isn't a post-hoc log - it's a real-time window into the agent's cognition during a FIRMS analysis, GOES scan, or self-initiated investigation.
Context Caching - Amortized System Context
cache, err := c.client.Caches.Create(ctx, "gemini-3-flash-preview", &genai.CreateCachedContentConfig{
DisplayName: "signet-system",
SystemInstruction: &genai.Content{
Parts: []*genai.Part{{Text: systemPrompt}},
},
Tools: Declarations(), // 28 tool declarations
TTL: 1 * time.Hour,
})
An agent running hundreds of cycles per day can't re-send a system prompt and 28 tool declarations every time. The system prompt and full tool schema are cached server-side with a 1-hour TTL, auto-refreshed 5 minutes before expiry. Every cycle references the cache by name. When a cycle needs a restricted tool set, it bypasses the cache and sends inline - the system dynamically chooses the optimal path per operation.
Multimodal Satellite Analysis
parts = append(parts, genai.NewPartFromText(prompt))
for sector, imgData := range images {
parts = append(parts, genai.NewPartFromBytes(imgData, "image/jpeg"))
}
GOES-19 Fire Temperature RGB images (1200×1200 JPG per sector, up to 10 sectors) are sent as native image parts alongside the structured text prompt. The model interprets thermal patterns directly - distinguishing bright clustered hot pixels (active fire) from diffuse warm regions (surface heating, arid land emissivity) - and correlates what it sees with FIRMS point detections and geospatial tool results in the same reasoning pass. This is spatial-temporal multimodal fusion: the model synthesizes visual satellite evidence with structured geospatial data to produce an assessment no single source could support.
Google Search Grounding
config := &genai.GenerateContentConfig{
Tools: []*genai.Tool{
{GoogleSearch: &genai.GoogleSearch{}},
},
}
Satellite data tells you there's heat. It doesn't tell you the fire's name, whether evacuations are underway, or that the road to the nearest town is closed. Before FIRMS and investigation cycles, Gemini queries Google Search via built-in grounding for real-time fire context - official names, containment percentages, evacuation orders, road closures. Sources are logged with full attribution in the reasoning feed.
Autonomous Tool Orchestration
Gemini 3's 1M token context window and advanced function calling make it possible to manage 28 tool declarations in a single session while accumulating multi-turn conversation history across 15+ sequential tool calls per cycle. A typical cycle sequences these calls entirely on its own, with thought signatures maintaining reasoning continuity across every step:
load_cycle_state → get_due_predictions → resolve_prediction → list_incidents → get_fire_imagery → create_goes_observation → get_weather → get_fire_weather_analysis → get_fuel_model → get_elevation → get_population → get_structures → create_incident → make_prediction → save_cycle_state → decide_live_cycle
This isn't a fixed sequence. The model skips tools that aren't relevant, calls some multiple times for different coordinates, and decides when to escalate versus dismiss. No routing logic in application code tells the model what to call next - orchestration is entirely within Gemini's reasoning, maintained by thought signatures across the full chain.
Data Sources
| Source | Provider | Data | Update Frequency |
|---|---|---|---|
| Thermal detections | NASA FIRMS | VIIRS/MODIS hotspots with FRP, confidence, coordinates | ~30 min |
| Satellite imagery | NOAA GOES-19 ABI | Fire Temperature RGB, 10 CONUS sectors | ~10 min |
| Weather | National Weather Service | Temp, humidity, wind, Haines Index, fire weather indices | Real-time |
| Elevation | USGS EPQS | Terrain altitude for fire behavior modeling | Static |
| Fuel models | LANDFIRE FBFM40 | 40 Scott & Burgan fuel categories | Static |
| Population | US Census | Block-level population counts | Static |
| Structures | OpenStreetMap Overpass | Building counts within configurable radius | Near real-time |
| Web context | Google Search | Active fire news, containment, evacuations | Real-time |
Tools (28)
| Category | Tools |
|---|---|
| Orchestration | decide_live_cycle, save_cycle_state, load_cycle_state |
| Incidents | create_incident, update_incident, list_incidents |
| Predictions | make_prediction, get_due_predictions, resolve_prediction |
| Satellite | get_fire_imagery, create_goes_observation, update_goes_observation, list_goes_observations, correlate_detection |
| Weather | get_weather, get_fire_weather_analysis, analyze_wind_fire_correlation |
| Terrain | get_elevation, get_fuel_model |
| Exposure | get_population, get_structures |
| Risk | create_risk_zone, update_risk_zone, list_risk_zones, validate_risk_zone |
| Calibration | get_alert_criteria, update_alert_criteria |
| Assessment | set_assessment_validity |
Usage
Production
The live system is running at signet.zachary.systems.
Development
git clone https://github.com/mapldx/signet
cd signet
# Set environment variables (either system-level, or create a .env)
export GEMINI_API_KEY=your_key
export FIRMS_MAP_KEY=your_firms_key
# Optional: if not set, will create a local SQLite database
export DATABASE_URL=your_postgres_url
# Optional: if not set, will default to 30-minute FIRMS/10-minute GOES cycles
export SIGNET_AUTONOMOUS=1
go run .
# Alternatively: append -dev for development mode, skipping AI calls
This project is built using:
| Category | Technologies |
|---|---|
| Backend | Go |
| AI | Gemini 3 (google.golang.org/genai) |
| Database | PostgreSQL (production), SQLite (development) |
| Frontend | HTMX, server-sent events |
Why This Matters
Fire detection is solved. NASA processes tens of millions of thermal detections annually. GOES images the continental US every 5 minutes. The data is abundant. What's missing is the step between "there's heat at these coordinates" and "here's what that means for the 2,400 people downwind."
That interpretation step - correlating a hotspot with wind, humidity, terrain slope, fuel type, population density, and existing incident history - currently requires a human analyst during business hours. Signet runs that workflow autonomously, 24/7, for every detection that crosses a threshold the model itself is tuning.
Three things make this more than a demo:
- It persists. State carries across cycles. The model remembers what it hypothesized last cycle, what it predicted, and what it learned. Thought signatures maintain reasoning continuity within cycles;
save_cycle_state/load_cycle_statemaintain it across them. This is a Marathon Agent - not a single inference call but a system that accumulates context over hours and days of continuous operation. - It self-corrects. Predictions are testable. "This fire will spread southwest within 6 hours" has a due date. When the due date arrives, the model checks new satellite data, scores itself, and adjusts thresholds if it's been wrong too often. Prediction accuracy below 50% triggers explicit recalibration via
update_alert_criteria. - It initiates. The most interesting cycles are the ones nobody requested. The model notices deteriorating weather in an area with high fuel loads and no active incidents, decides to investigate, and either confirms the risk or files it away as a learning. This is autonomous intelligence - not a pipeline waiting for input.
Disclaimer
Signet is not a substitute for official emergency information. Do not use this system to make evacuation, safety, or emergency response decisions.
For official wildfire information, refer to NIFC, InciWeb, and your local emergency management agency.
AI-generated assessments may contain errors. Satellite data may be delayed or incomplete. All analysis is provided as-is with no warranty.









Log in or sign up for Devpost to join the conversation.