import type { Metadata } from 'next'; import Link from 'next/link'; import { fmtDuration, fmtInterval, fmtUntil } from '@/components/meta/connectors-table'; import { ATTRIBUTION, FIELD_PRIORITY } from '@/components/meta/legal'; import { Callout } from '@/components/meta/prose'; import { FreshnessBadge } from '@/components/ui/badges'; import { Container, PageHeader, Section } from '@/components/ui/section'; import { Unavailable } from '@/components/ui/unavailable'; import { api, safe } from '@/lib/api'; import { fmtAgo, fmtDateTime, fmtInt } from '@/lib/format'; import { SITE_URL, routes } from '@/lib/site'; import type { SourceRow } from '@/lib/types'; export const metadata: Metadata = { title: 'Data sources — provenance, licenses and freshness', description: 'Every upstream source behind SatelliteIndex: CelesTrak GP and SATCAT today, planned government and scientific catalogs, with license, attribution, sync schedule and freshness.', alternates: { canonical: `${SITE_URL}/sources` }, openGraph: { title: 'Data sources | SatelliteIndex', description: 'Provenance, licenses, attribution and live freshness of every source feeding SatelliteIndex.', url: `${SITE_URL}/sources` }, twitter: { card: 'summary', title: 'Data sources | SatelliteIndex', description: 'Provenance, licenses, attribution and live freshness of every source feeding SatelliteIndex.' }, }; const AUTHORITY_LABEL: Record = { government: 'Government', intergovernmental: 'Intergovernmental', scientific: 'Scientific', secondary: 'Derived / secondary', commercial: 'Commercial' }; const AUTHORITY_TONE: Record = { government: 'var(--accent-2)', intergovernmental: 'var(--accent-2)', scientific: 'var(--accent)', secondary: 'var(--ink-3)', commercial: 'var(--warn)' }; function AuthorityBadge({ type, official }: { type: string | null; official: boolean }) { const t = type ?? 'unknown'; const color = AUTHORITY_TONE[t] ?? 'var(--inactive)'; return ( {AUTHORITY_LABEL[t] ?? t} {official && · official} ); } function SourceCard({ s, now }: { s: SourceRow; now: number }) { const planned = !s.enabled; return (

{s.name}

{s.type} {planned ? Planned connector : }
Website
{s.base_url ? ( {s.base_url.replace(/^https?:\/\//, '')} ) : ( '—' )}
License
{s.license ?? 'Unavailable'}
Attribution
{s.attribution_text ? {s.attribution_text} : '—'} {s.attribution_required && (required)}
Cadence
{s.update_frequency_seconds ? `Upstream refresh about every ${fmtInterval(s.update_frequency_seconds)}` : 'On demand (derived after each ingestion)'}
Priority
{s.priority}
{planned &&

Registered in the source catalog with its license and attribution, but no connector is running yet. Nothing on the site is sourced from it today.

}

Connectors

{!s.connectors || s.connectors.length === 0 ? (

No connector registered.

) : (
    {s.connectors.map((c) => (
  • {c.name} every {fmtInterval(c.interval_seconds)}
    {c.description &&

    {c.description}

    }
    Last sync
    {fmtAgo(c.last_success_at, now)}
    Next run
    {c.enabled ? fmtUntil(c.next_run_at, now) : 'disabled'}
    Duration
    {fmtDuration(c.last_duration_ms)}
    {(c.consecutive_failures > 0 || c.circuit_open_until) && ( <>
    Failures
    {fmtInt(c.consecutive_failures)} consecutive{c.circuit_open_until ? ` · circuit open until ${fmtDateTime(c.circuit_open_until)}` : ''}
    )}
  • ))}
)}

Raw snapshots

{fmtInt(s.raw_snapshots)}

Provenance rows

{fmtInt(s.provenance_rows)}

Last snapshot

{fmtAgo(s.last_snapshot_at, now)}

); } export default async function SourcesPage() { const res = await safe(api.sources()); const now = Date.now(); const sources = res?.data ?? null; const active = sources?.filter((s) => s.enabled) ?? []; const planned = sources?.filter((s) => !s.enabled) ?? []; return (

Live connector health: /status/data · How data is processed: /methodology {res && · generated {fmtDateTime(res.meta.generated_at)}}

{sources === null ? ( ) : ( <>
{active.map((s) => ( ))}

Sources are ordered by priority; a higher-priority source overrides a lower one for the fields it covers, and every accepted value is recorded in field_provenance with the source and observation time. Objects that disappear from a feed are never deleted.

{FIELD_PRIORITY.map((f) => ( ))}
Field Source Note
{f.field} {f.source} {f.note}
{planned.length > 0 && (
These catalogs are registered with their license and attribution so the schema is ready for them, but they are not ingested yet. They are listed here for honesty about coverage — no field on the site is currently attributed to them. {planned.map((s) => ( ))}
)}

{ATTRIBUTION}

If you reuse data from this site or its API, carry the attribution texts above forward — see the API page.

)}
); }