Skip to content

Repository files navigation

GeoVault

Fast, concurrent-safe MaxMind GeoIP lookups for Go — with automatic background updates.

Go Reference Go Report Card Go Version

GeoVault is a production-ready Go library for managing and querying MaxMind GeoIP2/GeoLite2 MMDB databases. It serves lookups straight from a locally cached database — no network round-trips — while a background updater keeps the data fresh.

Highlights

  • City & ASN lookups — full GeoLite2 City and ASN records, strongly typed
  • IPv4 & IPv6 — plus net.IP inputs and DNS resolution for hostnames
  • Zero network I/O during lookups — reads are pure local MMDB operations
  • Automatic background updates — HTTP metadata-based change detection
  • Atomic replacement — updates swap readers without interrupting lookups
  • Concurrent-safe — lookups, updates, and Close() race-tested with -race
  • SHA-256 integrity — hash verification as a final download check
  • Minimal dependencies — standard library plus one MMDB reader

Contents

Installation

go get github.com/obeliskdev/geovault

GeoLite2 databases are provided by MaxMind under a free license. Using the default download URLs means you agree to MaxMind's GeoLite2 end-user license agreement.

Quick start

package main

import (
	"fmt"
	"log"

	"github.com/obeliskdev/geovault"
)

func main() {
	geo, err := geovault.New()
	if err != nil {
		log.Fatal(err)
	}
	defer geo.Close()

	result, err := geo.Lookup("8.8.8.8")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Country.Code)      // US
	fmt.Println(result.City.Name)         // Mountain View
	fmt.Println(result.Location.Latitude) // 37.386
	fmt.Println(result.Location.Longitude)
	fmt.Println(result.ASN)               // 15169
	fmt.Println(result.Organization)      // Google LLC
}

geovault.New() downloads missing databases on first use, loads any valid local copies, and — when auto-update is enabled — refreshes them in the background.

Lookups

City

result, err := geo.LookupCity("8.8.8.8")

result.IP                   // "8.8.8.8"
result.Country.Code         // "US"
result.Country.Name         // "United States"
result.Country.Names        // map["en"]"United States", ...
result.Continent.Code       // "NA"
result.City.Name            // "Mountain View"
result.Subdivisions         // []Subdivision (full hierarchy)
result.Postal.Code          // "94043"
result.Location.Latitude    // 37.386
result.Location.Longitude   // -122.0838
result.Location.TimeZone    // "America/Los_Angeles"
result.Location.AccuracyRadius
result.Location.MetroCode
result.RegisteredCountry    // Country
result.RepresentedCountry   // Country
result.Traits               // MaxMind traits

Name is a convenience field derived from Names (English preferred); Names carries every localized value the database provides.

ASN

result, err := geo.LookupASN("8.8.8.8")

result.ASN                          // 15169
result.Organization                 // "Google LLC"
result.AutonomousSystemNumber       // 15169
result.AutonomousSystemOrganization // "Google LLC"

Unified

result, err := geo.Lookup("8.8.8.8")

The unified result combines City and ASN data whenever both databases are available; otherwise the available half is returned.

Convenience methods

geo.Lookup(addr)        // LookupResult (City + ASN)
geo.LookupCity(addr)    // CityResult
geo.LookupASN(addr)     // ASNResult
geo.LookupCountry(addr) // Country
geo.Country(addr)       // Country (alias)
geo.ASN(addr)           // ASNResult (alias)

IP literals, net.IP, and hostnames

Every method accepts an IP literal, and IP-suffixed variants accept a net.IP:

geo.LookupIP(net.ParseIP("8.8.8.8"))
geo.LookupCityIP(net.ParseIP("8.8.8.8"))
geo.LookupASNIP(net.ParseIP("8.8.8.8"))
geo.LookupCountryIP(net.ParseIP("8.8.8.8"))
geo.CountryIP(net.ParseIP("8.8.8.8"))
geo.ASNIP(net.ParseIP("8.8.8.8"))

When the string form receives a hostname, GeoVault resolves it via DNS before looking up, preferring an IPv4 address:

result, err := geo.Lookup("example.com") // result.IP is the resolved address

Lookups by IP literal are pure local MMDB reads with no network I/O; only hostname inputs trigger a DNS query.

Configuration

geo, err := geovault.New(
	geovault.WithDataDir("./geoip"),
	geovault.WithCityDatabaseURL("https://example.com/city.mmdb"),
	geovault.WithASNDatabaseURL("https://example.com/asn.mmdb"),
	geovault.WithAutoUpdate(true),
	geovault.WithUpdateInterval(12*time.Hour),
	geovault.WithCacheTTL(30*time.Minute),
)

Options

Option Description Default
WithDataDir(path) Directory for databases and metadata. OS cache dir
WithCityDatabaseURL(url) City database URL. MaxMind GeoLite2
WithASNDatabaseURL(url) ASN database URL. MaxMind GeoLite2
WithCityDatabaseFile(name) Local City filename. GeoLite2-City.mmdb
WithASNDatabaseFile(name) Local ASN filename. GeoLite2-ASN.mmdb
WithAutoUpdate(bool) Enable the background updater. false
WithUpdateInterval(d) Background update interval. 24h
WithCacheTTL(d) Cache duration for metadata checks. 1h
WithHTTPClient(c) Custom HTTP client for downloads. default client
WithTimeout(d) Timeout for the default HTTP client. 30s
WithUserAgent(s) User-Agent header. geovault
WithDownloadRetries(n) Download retry attempts. 3
WithRetryDelay(d) Delay between retries. 2s
WithLogger(l) Logger implementing Logger. no-op
WithOnUpdate(fn) Callback after a successful update.
WithOnError(fn) Callback on background update failure.

Updates

Automatic

geo, err := geovault.New(
	geovault.WithAutoUpdate(true),
	geovault.WithUpdateInterval(24*time.Hour),
)

The updater runs in its own goroutine and never blocks lookups. It ticks on the update interval, stops cleanly on Close(), never overlaps updates, and keeps the active database when an update fails.

Manual

err := geo.Update(ctx)     // check both (respects cache TTL)
err = geo.UpdateCity(ctx)  // check City only
err = geo.UpdateASN(ctx)   // check ASN only
err = geo.ForceUpdate(ctx) // check both, bypassing the cache TTL

Manual updates use the same safe mechanism as the background updater.

Custom URLs

geo, err := geovault.New(
	geovault.WithCityDatabaseURL("https://cdn.example.com/city.mmdb"),
	geovault.WithASNDatabaseURL("https://cdn.example.com/asn.mmdb"),
)

Cache

Successful metadata checks are cached for WithCacheTTL. Repeated Update calls within that window skip the remote check; use ForceUpdate to bypass it.

Update detection

When checking for remote changes, GeoVault applies, in order:

  1. Last-Modified — the HTTP header, via conditional If-Modified-Since requests and 304 Not Modified handling.
  2. Content-Length — remote size vs. local size, used only when no stronger metadata is available.
  3. Download + SHA-256 — download to a temp file and compare hashes, discarding the download when content is unchanged.

Downloads never touch the active file: GeoVault downloads to a .tmp file, verifies the response and size, computes the SHA-256, validates the MMDB structure, then atomically replaces the active database and switches the reader. Any failure preserves the existing database.

Thread safety

Client is safe for concurrent use, and lookups never hold locks across network operations — downloads never block IP lookups:

for i := 0; i < 100; i++ {
	go geo.Lookup(ip) // concurrent lookups
}
// plus a background update, plus Close() — all safe together

The package is verified with go test -race.

Errors

GeoVault exposes typed sentinel errors:

geovault.ErrDatabaseNotFound
geovault.ErrDatabaseInvalid
geovault.ErrDownloadFailed
geovault.ErrDatabaseUpdateFailed
geovault.ErrClosed
geovault.ErrInvalidIP

Inspect them with errors.Is:

if _, err := geo.Lookup("8.8.8.8"); errors.Is(err, geovault.ErrDatabaseNotFound) {
	// no database available
}

Logging

No external logging framework is required. Pass any logger implementing the four-level interface:

type Logger interface {
	Debug(msg string, args ...any)
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)
}

A no-op logger is used by default.

About

Production-ready Go library for fast MaxMind GeoIP2/GeoLite2 City and ASN lookups with automatic background database updates

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages