Self-hosted service
daimon3332/address avatar
daimon3332/address

daimon3332/address: a self-hosted residential address generator backed by open data and PostgreSQL

A self-hosted address and synthetic test-profile generator for 27 countries and regions, built from real open-data streets, administrative areas, coordinates, and postcodes. Supports multilingual output, IP-nearby generation, map previews, and API access. 基于真实开放数据的自托管地址与合成测试资料生成器,覆盖 27 个国家和地区,支持多语言地址、IP 附近生成、地图预览与 API 调用

2,149 stars288 forksTypeScriptMIT

At a glance

What is it?
Address publishes only address records that trace back to official registers, national statistics agencies or open-map residential evidence, and serves them from PostgreSQL. It is a tool for test fixtures and demos where a fake-looking address is not good enough.
Who is it for?
Adopt Address if you need test profiles and address fixtures that look like real places and you are willing to run PostgreSQL plus the Python-backed sync pipeline. Do not adopt it if you only need a few hundred fake strings, or if you cannot accept the provider keys and licence confirmations the sync layer asks for.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 18, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

Why a residential address generator needs real source records

Most address generators assemble plausible strings: a street name from one list, a house number from a range, a postcode from a pattern. The result is fine for a form with a length limit and useless for anything that has to resolve on a map, match a postcode directory, or survive a reviewer who checks whether the street exists.

Address takes the opposite position. Its README states that published residential base addresses come from official open data, national or regional address registers, map-registered buildings, and open-map records with explicit residential evidence. Records missing required fields, conflicting with administrative geography, falling outside valid coordinates, or lacking residential evidence are not published. The audience is engineers building test fixtures, demo tenants, QA datasets and profile seeds who need an address that a geocoder will actually place, and who would rather self-host than call a commercial geocoding API per record.

It is not a formatter and not a validator. If your problem is parsing a user-supplied address string, this project is the wrong shape entirely.

The 27-country scope and what each country actually contributes

The supported table covers North America (US, CA, MX), Europe (GB, DE, FR, IT, ES, NL, RU), East Asia (CN, HK, TW, JP, KR), Southeast Asia (SG, MY, TH, PH, VN), South Asia (IN), Oceania (AU), the Middle East (TR, SA), South America (BR) and Africa (NG, ZA).

What varies is the provenance. The United States draws on Overture Maps and state-level Geofabrik OSM shards, with residential evidence coming from explicit OSM or Overture residential building or use. Canada joins the Statistics Canada National Address Register with the same two map sources. Mexico uses the INEGI national address framework with INEGI TIPODOM=VIVIENDA as the residential test. The Netherlands reads Kadaster BAG through PDOK and requires an active BAG woonfunctie. France joins CSTB BDNB to BAN and falls back to map residential use where the join is unreliable. Spain uses Catastro INSPIRE address and building data with dwelling count as evidence.

The README is explicit that, apart from the identified China indoor fields, the generator does not randomly invent address components, and that a postcode or administrative area obtained by a unique match against an official directory still counts as a source-real field. China is the honest exception: administrative areas come from AreaCity/StatsGov, but community and building/unit/floor/room detail is enriched from AMap, Baidu and Tencent residential-community POIs. That is a different class of data from a national register, and the documentation says so rather than hiding it.

Strict filter semantics and the PostgreSQL random-selection path

Two design choices separate this from a generator that returns a string.

The first is filter behaviour. The README describes strict filter semantics: an empty matching pool returns an error instead of silently switching to another location. That is the right call for test data, because a silent fallback produces a test fixture that passes while testing the wrong region. It is also the behaviour most likely to surprise you on first use, when a narrow combination of country, administrative area and postcode returns nothing and you get an error rather than a nearby row.

The second is how rows are picked. Selection is database-backed across the complete eligible scope, and the README says it does not repeatedly read the first rows. The repository backs this with prebuilt random-address indexes and a PostgreSQL-only runtime with pooled connections and transactional publication. Publication being transactional matters for a sync pipeline that runs for hours: readers see the previous consistent set until the new one commits.

The runtime is PostgreSQL only. There is no SQLite or in-memory mode in the layout, which is a deliberate constraint rather than an oversight, and it means the smallest useful deployment includes a database container.

Installing Address with Docker Compose and generating a first profile

The repository ships a Dockerfile and a docker-compose.yml. The compose file defines an x-app anchor with the image daimon23/address:latest, mounts ./data/address, ./runtime and ./data/secrets, and expects secrets as files under /run/address-secrets rather than as plain environment values. The Dockerfile builds on node:24-bookworm, installs python3, python3-venv and zstd, creates a virtualenv at /srv/address/venv and installs server/sync/requirements.txt into it, because the synchronization layer is Python.

For local development outside containers, the .env.example gives the shape of the configuration. Note that the password is a placeholder you must replace, and that the master key is generated with openssl:

bash
openssl rand -base64 32

Paste the result into CONFIG_MASTER_KEY. The API listens on 127.0.0.1:8787 by default:

bash
API_HOST=127.0.0.1
API_PORT=8787
POSTGRES_URL=postgresql://address:REPLACE_WITH_PASSWORD@127.0.0.1:5432/address
POSTGRES_POOL_MAX=16

Then run the migrations and start the server. The package scripts are db:migrate and start:

bash
npm run db:migrate
npm run start

Before pulling any data, the ETL scripts support a dry run and a size estimate. Run these against a single country first, since a full bootstrap is the expensive path:

bash
npm run data:address-pool:sync:dry-run
npm run data:address-pool:estimate

When you are ready, data:address-pool:bootstrap runs the initial import with --initial --all. The README lists a JSON API covering health, readiness, countries, availability, location options, search, address and profile generation, batch generation and monitoring, with Python, cURL and JavaScript examples. Batch generation is bounded by BATCH_GENERATION_CONCURRENCY and API_MAX_INFLIGHT_GENERATIONS, both set to 4 in the example environment.

Provider keys, licence flags and the parts of the sync layer that cost money

The .env.example is the most revealing file in the repository, because it shows how much of the data pipeline depends on third-party services.

China community synchronization needs a server-side AMap WebService key, with numbered suffixes (AMAP_API_KEY_2 and so on) adding keys to a rotation pool. India uses a Mappls reverse geocoding key. Korea's initial import requires GEOGAPIFY_API_KEY, described as required for reverse-postcode geocoding with a free tier that is sufficient. Singapore uses an optional ONEMAP_ACCESS_TOKEN that expires after three days. There is also a Google Geocoding key for residential building enrichment and a browser-side AMap JS key with a security code.

The Mappls path carries explicit licence gates. Three variables default to false: ADDRESS_SYNC_MAPPLS_ENABLED, ADDRESS_SYNC_MAPPLS_LICENSE_CONFIRMED and ADDRESS_SYNC_MAPPLS_REDISTRIBUTION_ALLOWED. MAPPLS_MAX_REQUESTS_PER_RUN is 2000. That structure is a reasonable design: the project refuses to redistribute data whose licence you have not confirmed. It also means the India path is not something you switch on casually, and the README does not document what happens to already-synced rows if you later flip the redistribution flag back to false.

There is a credential broker in the compose file, addressed at http://credential-broker:8792 with a token file, which suggests secrets are meant to be brokered rather than read directly by the app. The README does not explain the broker's protocol.

Where Address is the wrong tool

The first limitation is operational weight. A PostgreSQL instance, a Python virtualenv, a Node 24 runtime, a sync scheduler and a credential broker are a lot of moving parts for generating addresses. If your test suite needs fifty plausible strings, the setup cost dwarfs the benefit.

The second is coverage asymmetry. The source table shows the United States and Canada resting on national registers plus map data, while other regions lean on OSM shards alone. Russia's residential evidence is an explicit OSM residential building, with no national register behind it. That means record density and freshness will not be uniform across the 27 entries, and the README does not publish per-country row counts. The coverage monitor exists precisely because coverage is uneven, but you have to run the sync before you know what you have.

The third is that map placement is not guaranteed. The README notes that every record retains source coordinates for positioning in services such as Google Maps or AMap where they cover the relevant region, and that text-search results depend on each platform's coverage, indexed names and update cycle. A record can be source-real and still fail to resolve by name in a given map product.

Finally, the documentation does not describe rollback for a failed or partial sync, beyond the fact that publication is transactional.

How this differs from Faker's address providers

The obvious alternative for test data is a general fake-data library such as Faker, which ships address providers for many locales. The difference is the mechanism, not the output shape.

Faker composes an address from locale-specific word lists and formats at request time. It is a pure function of a random seed, it needs no database, no network and no sync job, and it runs in milliseconds. Address instead pre-synchronizes a pool of records that came from registers and map data, stores them in PostgreSQL, and selects from that pool. The result is a string that corresponds to a real building with real coordinates, at the cost of a pipeline that has to be run, scheduled and monitored.

That trade is worth making when downstream systems resolve the address: a geocoder, a distance calculation, a postcode lookup, a map preview. It is not worth making when the address is only ever displayed or stored. A team already running PostgreSQL and needing realistic demo tenants is the natural fit; a team writing unit tests for a form validator is not.

The project also differs from calling a commercial geocoding API per record. Here the data lives in your database and the API surface is local, which removes per-request cost and rate limits but moves the sourcing, licensing and refresh burden onto you.

Editorial conclusion

Adopt Address if you need test profiles and address fixtures that look like real places and you are willing to run PostgreSQL plus the Python-backed sync pipeline. Do not adopt it if you only need a few hundred fake strings, or if you cannot accept the provider keys and licence confirmations the sync layer asks for. Before committing, run the dry-run and estimate scripts for one country and compare the published row count against the coverage monitor, then check the licence flags for the provider you intend to enable.

Frequently asked questions

What is an example of an address that Address generates?

The README does not print a sample record. It describes the fields per country, for example house number, street, city, state, ZIP and coordinates for the United States, all taken from Overture Maps and state-level Geofabrik OSM shards with explicit residential evidence.

How do I install Address?

The repository provides a Dockerfile and a docker-compose.yml using the image daimon23/address:latest, with PostgreSQL and secrets mounted under /run/address-secrets. For local development the .env.example sets API_HOST to 127.0.0.1 and API_PORT to 8787, and the package scripts db:migrate and start bring up the schema and server.

How do I use Address to generate a profile?

Start the API, then call the JSON generation endpoint documented in the README, which also lists Python, cURL and JavaScript examples. Batch generation is available and is bounded by BATCH_GENERATION_CONCURRENCY and API_MAX_INFLIGHT_GENERATIONS, both set to 4 in .env.example.

How do I write an address with Address?

Address does not compose an address from parts. It selects a published record from PostgreSQL that already contains the source fields, so the wording and order come from the underlying register or map data rather than from a template.

What does the address mean in this project?

Here an address is a residential base record with source coordinates, drawn from official open data, national or regional address registers, map-registered buildings, or open-map records with explicit residential evidence. Records lacking residential evidence are not published.

Official sources

  1. daimon3332/address on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes