Skip to content
NewYour data: charts, lists and meters that take your records
<GeoThreats />ReactAccess & securityFree

Attack origins

Inbound requests converge on a protected site at the centre of a dotted globe. Blocked ones dent its shield ring and burst off it.

View as Markdown
<GeoThreats />

Trypress Flood and watch the shield take the hits.

Your data

Pass your requests as events, or call push() on the ref for a live feed. Without events the component shows a seeded demo stream with invented origins and prefilled demo counts. See Your data.

Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

Loading <GeoThreats />
Props
maxInFlightlive
windowSecondslive
shieldRadiuslive
livelive
React
<GeoThreats maxInFlight={24} windowSeconds={60} shieldRadius={26} live />

Installation

pnpm dlx shadcn@latest add @lumesec/geo-threats
First timeRegister the @lumesec registry once, or install by URL+

Adds @lumesec to your components.json:

pnpm dlx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json

Or skip the setup and install by URL:

npx shadcn@latest add https://elements.lumesec.ai/r/geo-threats.json

Usage

geo-threats-example.tsx
import { GeoThreats, type ThreatEvent } from "@/components/lumesec/geo-threats";

export function Example({ requests }: { requests: ThreatEvent[] }) {
  return <GeoThreats target={{ label: "eu-central", lat: 50.11, lon: 8.68 }} events={requests} />;
}

Behaviour

Inbound requests from around the world converge on one protected site at the centre of a globe; requests from the far side come up over the horizon. A ring of dots around the site is its shield: blocked requests hit the ring and burst off it, allowed requests pass through and land.

Where a request hits, the shield dents and lights up for a moment. Counts per verdict and the top origin regions roll beside the globe; arcs in flight are capped and the rest are folded into the counts.

API reference

Props

Also accepts every prop of <div> (React.ComponentProps<"div">), spread onto the root element.

targetTypeThreatTargetDefault{ label: "eu-central", lat: 50.1109, lon: 8.6821 }

{ label, lat, lon } of the protected site. The globe faces it, so it sits at the centre of the disc; the label shows in the header and the accessible summary.

eventsTypereadonly ThreatEvent[]

Requests: { id, lat, lon, verdict, region?, time? } with verdict blocked or allowed. Each new id animates once, so the array can grow or be replaced. Without it a seeded demo stream runs; pass an empty array to show only what you push().

maxInFlightTypenumberDefault24

Arcs drawn at once. Further requests go straight into the counts.

windowSecondsTypenumberDefault60

Window for the counts and the origin list, in seconds. Requests older than the window are ignored.

shieldRadiusTypenumberDefault26

Radius of the shield ring in px, from 10 to 120 and at most 60% of the globe's radius.

liveTypebooleanDefaulttrue

Runs the demo stream while events is not given. Off keeps the prefilled demo counts without new requests.

onEventType(event: ThreatEvent) => void

Called when a request reaches the shield (blocked) or the site (allowed). Requests without an arc call it at once.

refTypeReact.Ref<GeoThreatsHandle>

Exposes push().

Ref

ref receives a GeoThreatsHandle handle with these methods.

push(event: ThreatEvent): void

Adds one request, with or without events. An id that was already seen is ignored.

Accessibility

  • The globe stage has role="img" and an aria-label that summarises the window, for example "Requests to eu-central. In the last minute: 1,284 blocked, 312 allowed, most from Southern Asia." It is refreshed at most every 15 seconds.
  • The counters and the origin list are real text. Each origin row has a visually hidden sentence with its region, total, blocked and allowed counts; the rolling digits and the dotted bars are aria-hidden.
  • Both canvases, the server placeholder and the target square are aria-hidden. The globe takes no pointer or keyboard input.
  • Reduced motion: no arcs, dents or sparks play: each request lights the shield dots facing its origin for 400 ms and is counted at once, the counts swap without rolling and the origin rows move without sliding.

Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from --lumesec. See Theming.

This component reads --lumesec, --lumesec-glint, --lumesec-shine, --lumesec-success, --destructive, --foreground, --muted-foreground, --card and --border.

Notes

  • The globe always faces the target and never turns, so each arc is projected once. Arcs rise higher the farther they travel; origins on the far side climb over the horizon, where a short glint marks the arc.
  • Packets speed up toward the site: 1 s plus up to 0.8 s for the far side of the world. A blocked packet stops at the ring: the dots around the impact dent inward on springs, heat up and throw ten sparks back the way it came. An allowed packet parts the ring, passes and lands with a small ring of lit dots around the site.
  • Requests are grouped by region. Without it, the group is the world region of the origin (such as Southern Asia or South America) from the embedded country data; points at sea take the nearest country's region.
  • Counts cover the last windowSeconds, by each request's time (or when it arrived). The origin list shows the five regions with the most requests and re-sorts at most once a second; each row has a dotted bar with the region's share of all requests, blocked dots first.
  • Requests older than 10 seconds, requests that arrive while the globe is off screen or the tab is hidden, and requests beyond maxInFlight are counted without an arc. Ids are remembered (the last 4,000), so a re-render with the same array never counts them twice.
  • The stage is square, at most 420 px wide; at 560 px of container width and above the origin list stands beside it, else below. The arcs and sparks draw on a canvas that reaches 40 px past the stage on every side.
  • The server markup shows the disc with a 30° graticule, the shield ring and the target; it fades to the dotted globe after the first frame. The animation loop pauses while the globe is off screen and stops when nothing moves.
  • The demo stream sends about four requests a second, 80% of them blocked, from invented sources around real cities, and prefills a window of counts on mount.

Was this page helpful?