Limit a space to some networks

Embed
Preview of the card
<a href="https://forumulate.com/guides/network-allow-list"><img src="https://forumulate.com/guides/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/36aa4df0-1e95-4cef-8ae9-795169441345/pages/989c2661-9a3e-47b5-a8d0-0d019c9c4f4b/social-card.svg?v=3e7ae33eec8b" alt="Limit a space to some networks" width="600" height="315"></a>

Some spaces are meant for one building or one VPN: an internal handbook, an IT help desk FAQ, a roadmap for staff. Making every reader sign in is one way to keep them in. A network allow-list is the other: the space stays public in the sense that nobody has to sign in, but only readers connecting from addresses you list can open it. Anyone else gets a page that says the space is not available from their network.

There is one allow-list per space, settings.networkAccess.ipAllowList. Status pages are the exception and keep a list of their own (see Status pages below).

Which spaces it applies to

Space type

List

Where you set it

Documentation, Community spaces, blog, changelog, roadmap, FAQ, events, courses, legal, careers, contact, custom

settings.networkAccess.ipAllowList

PUT …/network-access, studio Settings → Network access

Status page

settings.statusAccess.ipAllowList (its own)

PUT …/status/access, the status page's own panel

Content library

none — it has no reader to gate

—

The list only matters for a public space. A private space is already closed to everyone without a share link, and a share-link preview of a private space is not asked about the network: you handed that link out yourself.

Defaults

  • Unset or empty means everyone. A space you never touched is readable from anywhere, exactly as before. There is no organization-wide default; each space decides for itself.

  • The list applies to everyone who reads, including you. It is checked on the reader, not on your account, so an owner reading the published space from a café is refused like anyone else. Make sure your own network is on the list before you save. Your console and the authenticated API (/orgs/{orgId}/…) are not affected, so you can always fix the list.

  • The network is checked first. A reader from elsewhere is refused before a space password or reader sign-in is asked for, so the sign-in screen never appears to someone who could not read the space anyway.

Setting the list

Each entry is an IPv4 or IPv6 address, or a CIDR block: 203.0.113.7, 203.0.113.0/24, 2001:db8::/32. Up to 200 entries. An entry nothing can parse is refused with a 422 naming it, rather than saved to silently never match. Duplicates are dropped. PUT replaces the whole list; send [] to open the space to everyone again.

curl -X PUT "https://forumulate.com/api/orgs/$ORG/spaces/$SPACE/network-access" \
  -H "Authorization: Bearer $FORUMULATE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "ipAllowList": ["203.0.113.0/24", "198.51.100.14", "2001:db8:42::/48"] }'
{ "ipAllowList": ["203.0.113.0/24", "198.51.100.14", "2001:db8:42::/48"] }

GET on the same address answers the stored list (space.read); PUT needs space.update. Sending the list to a status page's network-access is a 422 that points at …/status/access. See Space network access.

Which address to list: the public address your office or VPN reaches the internet from (its egress or NAT address), not the private 10.x / 192.168.x address a laptop has on the office network. Saving the list empties every cached copy of the space's pages, so the change applies at once.

In the studio

Open the space's studio, then Settings → Network access. Each network is its own row, so when the API refuses one entry you can see which; Add a network adds a row, and the panel reminds you to include your own network. The section is on every space type except status pages (which have their own panel) and content libraries.

What a refused reader sees

A reader page draws a short notice instead of the space: "Not available from this network", with a line suggesting they connect to their organisation's network or VPN and reload. It is translated into English, German, Spanish, French and Portuguese. It never shows the list, and it never says which addresses would be allowed.

What else the list covers

The gate runs in the API's public read path, so it covers every way of reading the space, not just its pages:

Surface

From an allowed network

From anywhere else

Reader pages (every page, hub, tag page, print view)

served

the "not available" notice

Public JSON (/public/orgs/{orgId}/spaces/{spaceId}/…)

served

403 space.network_refused

A page's markdown twin (<page>.md)

served

403 space.network_refused

Feeds (rss.xml, updates.xml, a page's update feed)

served

403 space.network_refused

Widgets and embeds (search box, Ask AI launcher, FAQ widget)

served

refused the same way

Sitemap, robots.txt, llms.txt

the space is left out

the space is left out

Two behaviours follow from it on purpose:

  • A limited space is never advertised. Sitemaps, robots.txt and llms.txt are fetched by crawlers, which are not on your network, so a space with a list is left out of them for everyone. Search engines will not index it.

  • A limited space is never kept in a shared cache. Every answer depends on who asked, so gated responses are marked private, no-store and the edge never serves one reader's copy to another. A space whose list was just added is treated as limited for up to a minute while the edge learns about it; the cost is a few uncached renders, never a leaked page.

A feed reader or a markdown fetch made from a server elsewhere (a hosted feed reader, an AI tool's crawler) is refused like a browser from elsewhere.

Checking from where you are

Two public addresses, both without a token:

# Is this space limited at all? Facts only: the list is never published.
curl "https://forumulate.com/api/public/orgs/$ORG/spaces/$SPACE/network-check/facts"
# { "network": true }

# May I read it from here?
curl "https://forumulate.com/api/public/orgs/$ORG/spaces/$SPACE/network-check"
# 200 { "ok": true }, or 403 space.network_refused

network-check runs the space's whole public gate for the caller: a space behind a password or reader sign-in answers 401 as any read of it does, and a space the caller may not read at all answers 404. It is never cached. See Public network.

Status pages keep their own list

A status page has the same kind of list with the same rules and the same parser, stored as settings.statusAccess.ipAllowList and set at GET/PUT /orgs/{orgId}/spaces/{spaceId}/status/access (Status access). It covers the page, its JSON, feeds, badge and widget. The space allow-list above is never applied to a status page, so a status page is not checked twice. See Status pages.

In the API

Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.

Status Access

Operation

Request

What it does

Shortcuts

StatusAccess_read

GET /orgs/{orgId}/spaces/{spaceId}/status/access

A status page's network allow-list

client.status.access.read()

StatusAccess_write

PUT /orgs/{orgId}/spaces/{spaceId}/status/access

Set a status page's network allow-list

client.status.access.write()

The Forumulate teamUpdated