@mountainpass/addressr 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,38 @@
1
1
  # Addressr
2
2
 
3
- ![Addressr](https://addressr.io/icons/icon-144x144.png Addressr)
3
+ ![Addressr](https://addressr.io/icons/icon-144x144.png 'Addressr')
4
4
 
5
- [Australian Address Validation, Search and Autocomplete](https://addressr.io) - [addressr.io](https://addressr.io)
5
+ **The only open-source, free self-hosted Australian address validation API.**
6
6
 
7
- [![GitHub license](https://img.shields.io/github/license/mountain-pass/addressr)](https://github.com/mountain-pass/addressr/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/@mountainpass/addressr)](https://www.npmjs.com/package/@mountainpass/addressr) [![npm downloads](https://img.shields.io/npm/dm/@mountainpass/addressr)](https://www.npmjs.com/package/@mountainpass/addressr)
7
+ [Australian Address Validation, Search and Autocomplete](https://addressr.io) [addressr.io](https://addressr.io)
8
8
 
9
- [![Maintainability](https://api.codeclimate.com/v1/badges/e5117809cacb7e32eb5c/maintainability)](https://codeclimate.com/github/mountain-pass/addressr/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/e5117809cacb7e32eb5c/test_coverage)](https://codeclimate.com/github/mountain-pass/addressr/test_coverage) ![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m788652244-3e35661f9886333310f4dc2f)
9
+ [![GitHub license](https://img.shields.io/github/license/mountain-pass/addressr)](https://github.com/mountain-pass/addressr/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/@mountainpass/addressr)](https://www.npmjs.com/package/@mountainpass/addressr) [![npm downloads](https://img.shields.io/npm/dm/@mountainpass/addressr)](https://www.npmjs.com/package/@mountainpass/addressr)
10
10
 
11
11
  [![GitHub issues](https://img.shields.io/github/issues/mountain-pass/addressr)](https://github.com/mountain-pass/addressr/issues) [![GitHub pull requests](https://img.shields.io/github/issues-pr/mountain-pass/addressr)](https://github.com/mountain-pass/addressr/pulls)
12
12
 
13
13
  # About
14
14
 
15
- Australian Address Validation, Search and Autocomplete powered by the Geocoded National Address File (G-NAF), Australias **authoritative** address database with 15+ million addresses.
15
+ Australian Address Validation, Search and Autocomplete powered by the Geocoded National Address File (G-NAF), Australia's **authoritative** address database with 15+ million addresses.
16
+
17
+ - **Validated addresses** from the official G-NAF source
18
+ - **Real-time autocomplete** with fuzzy matching
19
+ - **Locality, postcode, and state search** for area pickers
20
+ - **Geocoding** to latitude/longitude (optional)
21
+ - **Self-hosted or SaaS** — your choice, your data
22
+ - **Open source** — audit the code, customize as needed
23
+
24
+ # Why Addressr
25
+
26
+ | | Addressr | [Addressify](https://addressify.com.au/) | [AddressFinder](https://addressfinder.com.au/) | [Geoscape](https://geoscape.com.au/) | Google Maps |
27
+ | ---------------------------------- | -------- | ---------------------------------------- | ---------------------------------------------- | ------------------------------------ | ----------- |
28
+ | Self-hosted | ✅ | ❌ | ❌ | ❌ | ❌ |
29
+ | Open source | ✅ | ❌ | ❌ | ❌ | ❌ |
30
+ | Free tier (unlimited, self-hosted) | ✅ | ❌ | ❌ | ❌ | ❌ |
31
+ | G-NAF data source | ✅ | ✅ | ✅ | ✅ (creator) | ❌ |
32
+ | Data sovereignty | ✅ | ❌ | ❌ | ❌ | ❌ |
33
+ | MCP integration for AI assistants | ✅ | ❌ | ❌ | ❌ | ❌ |
34
+
35
+ **Stop paying Google Maps for Australian addresses.** Stop locking your data into third-party SaaS. Addressr gives you unlimited address validation on your own infrastructure, or a cheap hosted API if you prefer.
16
36
 
17
37
  # Quick Start
18
38
 
@@ -85,15 +105,15 @@ Run Addressr on your own infrastructure for full control over your data.
85
105
  export ADDRESSR_INDEX_BACKOFF_MAX=10000
86
106
  ```
87
107
 
88
- 1. Optional - enable geocodes by setting the following env vars for the data loader. In the third window run:
89
- **NOTE:** with geocodes enabled, indexing takes much longer and needs much more memory. Only use turn them on if you need them. You can always add them later.
108
+ 1. Optional enable geocodes by setting the following env vars for the data loader. In the third window run:
109
+ **NOTE:** with geocodes enabled, indexing takes much longer and needs much more memory. Only turn them on if you need them. You can always add them later.
90
110
 
91
111
  ```
92
112
  export ADDRESSR_ENABLE_GEO=1
93
113
  export NODE_OPTIONS=--max_old_space_size=8196
94
114
  ```
95
115
 
96
- 2. Optional - limit the addresses to a single state by setting the `COVERED_STATES` env var for the data loader.
116
+ 2. Optional limit the addresses to a single state by setting the `COVERED_STATES` env var for the data loader.
97
117
  This dramatically speeds up indexing. For example, in the third window run:
98
118
 
99
119
  ```
@@ -117,7 +137,8 @@ Run Addressr on your own infrastructure for full control over your data.
117
137
  addressr-loader
118
138
  ```
119
139
 
120
- 6. OK, so we stretched the truth a bit with the "Quick Start" heading. The truth is that it takes quite a while to download, store and index the 13+ million addresses from [data.gov.au](http://data.gov.au/). So make a coffee, or tea, or find something else to do and come back in about an hour when it's done.
140
+ 6. OK, so we stretched the truth a bit with the "Quick Start" heading. The truth is that it takes quite a while to download, store and index the 15+ million addresses from [data.gov.au](http://data.gov.au/). So make a coffee, or tea, or find something else to do and come back in about an hour when it's done.
141
+
121
142
  7. Search for an address using the command line
122
143
 
123
144
  ```
@@ -125,7 +146,24 @@ Run Addressr on your own infrastructure for full control over your data.
125
146
  ```
126
147
 
127
148
  8. An updated G-NAF is released every 3 months. Put `addressr-loader` in a cron job or similar to keep addressr regularly updated
128
- 9. Wire you address form up to the address-server api.
149
+ 9. Wire your address form up to the address-server api.
150
+
151
+ # API Endpoints
152
+
153
+ Addressr exposes a HATEOAS REST API. Start at the root (`/`) and follow links to discover endpoints. A supplementary OpenAPI 3.x spec is available at `/api-docs`.
154
+
155
+ | Endpoint | Purpose | Example |
156
+ | ---------------------------- | ------------------------------------------------------------------ | --------------------------------- |
157
+ | `GET /addresses?q=` | Search and autocomplete addresses | `/addresses?q=1+george+st+sydney` |
158
+ | `GET /addresses/{pid}` | Get full address details (with links to locality, postcode, state) | `/addresses/GAOT_717882967` |
159
+ | `GET /localities?q=` | Search suburbs/localities by name | `/localities?q=lilydale` |
160
+ | `GET /localities/{pid}` | Get locality details (with links to postcode, state) | `/localities/loc9984d8beb142` |
161
+ | `GET /postcodes?q=` | Search postcodes (q optional) | `/postcodes?q=3140` |
162
+ | `GET /postcodes/{postcode}` | Get postcode with associated localities | `/postcodes/6798` |
163
+ | `GET /states?q=` | Search states/territories (q optional) | `/states?q=New` |
164
+ | `GET /states/{abbreviation}` | Get state details | `/states/NSW` |
165
+ | `GET /api-docs` | OpenAPI 3.x specification | `/api-docs` |
166
+ | `GET /health` | Health check | `/health` |
129
167
 
130
168
  ## How it Works
131
169
 
@@ -148,6 +186,30 @@ Run Addressr on your own infrastructure for full control over your data.
148
186
  | ADDRESSR_ACCESS_CONTROL_EXPOSE_HEADERS | _non-blank_ | An `Access-Control-Expose-Headers` response header is returned with the value in the environment variable | |
149
187
  | ADDRESSR_ACCESS_CONTROL_ALLOW_HEADERS | _blank_ | An `Access-Control-Allow-Headers` response header is **not** returned | ✅ |
150
188
  | ADDRESSR_ACCESS_CONTROL_ALLOW_HEADERS | _non-blank_ | An `Access-Control-Allow-Headers` response header is returned with the value in the environment variable | |
189
+ | ADDRESSR_PROXY_AUTH_HEADER | _blank_ | No gateway auth header enforcement (self-hosted default) | ✅ |
190
+ | ADDRESSR_PROXY_AUTH_HEADER | _non-blank_ | Name of the header the origin requires — set alongside `ADDRESSR_PROXY_AUTH_VALUE` | |
191
+ | ADDRESSR_PROXY_AUTH_VALUE | _blank_ | No gateway auth header enforcement (self-hosted default) | ✅ |
192
+ | ADDRESSR_PROXY_AUTH_VALUE | _non-blank_ | Expected value the header must carry — set alongside `ADDRESSR_PROXY_AUTH_HEADER` | |
193
+
194
+ ### Gateway auth header (optional)
195
+
196
+ By default Addressr does not enforce any proxy authentication — self-hosted npm and Docker deployments work with zero configuration.
197
+
198
+ If you front Addressr with an API gateway (RapidAPI, Kong, Tyk, Apigee, AWS API Gateway, nginx, Caddy, or your own Cloudflare Worker) and want the origin to reject traffic that bypasses your gateway, set both environment variables to the header name your gateway injects and the shared secret it forwards:
199
+
200
+ | Variable | Example | Notes |
201
+ | ---------------------------- | ------------------------- | ------------------------------------------- |
202
+ | `ADDRESSR_PROXY_AUTH_HEADER` | `X-RapidAPI-Proxy-Secret` | Header name your gateway forwards |
203
+ | `ADDRESSR_PROXY_AUTH_VALUE` | `<your-gateway-secret>` | Expected value; keep out of version control |
204
+
205
+ Behaviour:
206
+
207
+ - Both unset → no enforcement (default).
208
+ - Both set → requests without a matching header receive `401 Authentication required`.
209
+ - Exactly one set → the process exits at startup with a clear error (fails loud to prevent silent bypass).
210
+ - `/health` and `/api-docs` remain reachable without the header so uptime monitors and gateway OpenAPI imports keep working.
211
+
212
+ See [ADR 024](docs/decisions/024-origin-gateway-auth-header-enforcement.proposed.md) for the decision record.
151
213
 
152
214
  NOTE: When adjusting PAGE_SIZE, you should take into account how quickly you want the initial results returned to the user. In many use cases, you want this to be as fast as possible. If you need show more results to the user, you are often better off leaving it a 8 and using the paging links to get more results while you are displaying the first 8.
153
215
 
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.proxyAuthMiddleware = proxyAuthMiddleware;
7
+ exports.validateProxyAuthConfig = validateProxyAuthConfig;
8
+ var _debug = _interopRequireDefault(require("debug"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ /* eslint-disable @eslint-community/eslint-comments/disable-enable-pair */
11
+ /* eslint-disable security/detect-object-injection -- env var names are compile-time constants, not user input */
12
+
13
+ const error = (0, _debug.default)('error');
14
+ error.log = console.error.bind(console);
15
+ const HEADER_VAR = 'ADDRESSR_PROXY_AUTH_HEADER';
16
+ const VALUE_VAR = 'ADDRESSR_PROXY_AUTH_VALUE';
17
+
18
+ // Closed list per ADR 024: /health for monitoring, /api-docs for gateway
19
+ // OpenAPI imports (ADR 023). Exact-match, not prefix.
20
+ const ALLOWLIST = new Set(['/health', '/api-docs']);
21
+ function isNonEmpty(value) {
22
+ return typeof value === 'string' && value.length > 0;
23
+ }
24
+ function validateProxyAuthConfig(environment = process.env) {
25
+ const headerSet = isNonEmpty(environment[HEADER_VAR]);
26
+ const valueSet = isNonEmpty(environment[VALUE_VAR]);
27
+ if (headerSet && !valueSet) {
28
+ throw new Error(`Proxy auth misconfigured: ${HEADER_VAR} is set but ${VALUE_VAR} is missing. Set both to enforce a gateway auth header, or unset both to disable enforcement.`);
29
+ }
30
+ if (valueSet && !headerSet) {
31
+ throw new Error(`Proxy auth misconfigured: ${VALUE_VAR} is set but ${HEADER_VAR} is missing. Set both to enforce a gateway auth header, or unset both to disable enforcement.`);
32
+ }
33
+ }
34
+ function proxyAuthMiddleware() {
35
+ return function proxyAuth(request, response, next) {
36
+ const headerName = process.env[HEADER_VAR];
37
+ const expected = process.env[VALUE_VAR];
38
+ if (!isNonEmpty(headerName) || !isNonEmpty(expected)) {
39
+ return next();
40
+ }
41
+ if (ALLOWLIST.has(request.path)) {
42
+ return next();
43
+ }
44
+ const presented = request.get ? request.get(headerName) : undefined;
45
+ if (presented === expected) {
46
+ return next();
47
+ }
48
+ error('proxy-auth rejected %s', request.path);
49
+ return response.status(401).json({
50
+ message: 'Authentication required'
51
+ });
52
+ };
53
+ }
@@ -12,6 +12,7 @@ var _waycharter = require("@mountainpass/waycharter");
12
12
  var _addressService = require("../service/address-service");
13
13
  var _version = require("../version");
14
14
  var _nodeCrypto = _interopRequireDefault(require("node:crypto"));
15
+ var _proxyAuth = require("./proxy-auth");
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  //import connect from 'connect';
17
18
 
@@ -335,7 +336,7 @@ function buildOpenApiSpec(apiVersion) {
335
336
  openapi: '3.0.3',
336
337
  info: {
337
338
  title: 'Addressr by Mountain Pass',
338
- description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract. Follow `related` Link headers to navigate between addresses, localities, postcodes and states.',
339
+ description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract. Follow `related` Link headers to navigate between addresses, localities, postcodes and states.\n\nDirect requests to upstream hosts may be rejected when the operator has configured a gateway auth header. Consumers should always call Addressr through its published gateway endpoint; monitoring (`/health`) and spec discovery (`/api-docs`) remain openly reachable.',
339
340
  version: apiVersion
340
341
  },
341
342
  servers: [{
@@ -657,6 +658,7 @@ error.log = console.error.bind(console);
657
658
  let server;
658
659
  const PAGE_SIZE = process.env.PAGE_SIZE || 8;
659
660
  function startRest2Server() {
661
+ (0, _proxyAuth.validateProxyAuthConfig)();
660
662
  app.use((_request, response, next) => {
661
663
  if (process.env.ADDRESSR_ACCESS_CONTROL_ALLOW_ORIGIN !== undefined) {
662
664
  response.append('Access-Control-Allow-Origin', process.env.ADDRESSR_ACCESS_CONTROL_ALLOW_ORIGIN);
@@ -669,6 +671,7 @@ function startRest2Server() {
669
671
  }
670
672
  next();
671
673
  });
674
+ app.use((0, _proxyAuth.proxyAuthMiddleware)());
672
675
  const waycharter = new _waycharter.WayCharter();
673
676
  app.use(waycharter.router);
674
677
  const addressesType = waycharter.registerCollection({
package/lib/version.js CHANGED
@@ -5,4 +5,4 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = void 0;
7
7
  // Generated by genversion.
8
- const version = exports.version = '2.1.3';
8
+ const version = exports.version = '2.1.5';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mountainpass/addressr",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Australian Address Validation, Search and Autocomplete",
5
5
  "author": {
6
6
  "name": "Mountain Pass",
@@ -207,7 +207,7 @@
207
207
  "dry-aged-deps": "^2.6.0",
208
208
  "eslint": "^9.39.4",
209
209
  "eslint-config-prettier": "^10.1.8",
210
- "eslint-plugin-chai-friendly": "^1.1.0",
210
+ "eslint-plugin-chai-friendly": "^1.2.0",
211
211
  "eslint-plugin-import-x": "^4.16.2",
212
212
  "eslint-plugin-n": "^17.24.0",
213
213
  "eslint-plugin-prettier": "^5.5.5",