> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Networking

> VPCs, subnets and interfaces — the private address space your instances run in.

A VPC is a private network in one region, carved into subnets, with interfaces
(NICs) allocated out of those subnets. Nothing in a VPC can reach the internet
or be reached from it until you add a gateway and a route, which is deliberate:
see [gateways and routing](/networking/gateways).

The service is **regional**. The endpoint carries the region:
`https://network.sa-saopaulo-1.basaltic.sh`.

<CardGroup cols={2}>
  <Card title="VPCs" icon="box" href="#vpcs">
    Why the CIDR has to be private, and the one field you can only set at
    create.
  </Card>

  <Card title="Subnets" icon="grid-2x2" href="#subnets">
    What "public" actually means, which addresses you don't get, and what
    blocks a delete.
  </Card>

  <Card title="Interfaces" icon="network" href="#interfaces">
    A NIC with its own identity, and what survives a detach from an instance.
  </Card>

  <Card title="Names and access" icon="shield" href="#names-crns-and-tag-conditioned-access">
    Which resources get name-based CRNs, and where a policy wildcard is wider
    than it looks.
  </Card>
</CardGroup>

## VPCs

<Tabs>
  <Tab title="Console">
    Go to **Networking → VPCs** and choose **Create VPC**. Under **VPC
    details**, give it a **Name** and an **IPv4 CIDR**.

    The page is a workflow rather than a single create. **Subnets** asks how
    many **Public subnets** and **Private subnets** to carve out of that CIDR
    and lets you edit each generated **Name** and **IPv4 CIDR**, and
    **Internet access** offers an **Internet gateway** and a **NAT gateway**.
    Submitting hands you to a **Provisioning progress** page that walks the
    plan one step at a time — **Create VPC**, **Create public route table**,
    **Create internet gateway**, **Attach internet gateway**, **Configure
    public internet route**, then one step per subnet, and **Create NAT
    gateway** with **Configure private internet route** if you asked for NAT.

    <Warning>
      That sequence runs in your browser, which is why the page says **Keep
      this page open until provisioning finishes.** Navigate away mid-run and
      whatever was already created stays, but the remaining steps never
      happen. Come back to the same progress page and it offers **Retry
      failed step**, picking up where it stopped.
    </Warning>
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST https://network.sa-saopaulo-1.basaltic.sh/v1/vpcs
    {
      "name": "prod",
      "cidr_v4": "10.0.0.0/16"
    }
    ```

    One call, one VPC. The subnets, gateway and routes the console workflow
    adds are separate calls you make yourself — see
    [gateways and routing](/networking/gateways).
  </Tab>
</Tabs>

`name` is 1–63 characters, lowercase letters, digits and hyphens, and cannot
start or end with a hyphen. It is unique per account and it lands in the VPC's
CRN.

Creating a VPC also creates its `main` route table. Subnets land there unless
you say otherwise.

### The CIDR must be private

`cidr_v4` has to sit inside `10.0.0.0/8`, `172.16.0.0/12` or `192.168.0.0/16`.
The reason is not tidiness: addressing your instances out of routable space you
do not own would blackhole that space for your own workloads, and for anyone
else's once the VPC routes anywhere. Public reachability comes from
[a floating IP or a gateway](/networking/gateways), never from the instance's
own IPv4 address.

<Warning>
  The **whole prefix** has to fall inside one private block, not just its first
  address. `10.0.0.0/7` starts in RFC 1918 and spans out of it, so it is
  rejected.
</Warning>

`name` and `cidr_v4` are immutable. `PATCH /v1/vpcs/{vpc_id}` takes
`description` and `tags` and nothing else — there is no VPC resize.

### IPv6 is a create-time decision

<Tabs>
  <Tab title="Console">
    On **Create VPC**, under **VPC details**, turn on the **IPv6** field's
    **Assign a Basaltic /60** switch before you submit.

    <Warning>
      The text box beside that switch is not a second way to get IPv6. The API
      accepts no prefix of yours, so a prefix typed there is dropped and the
      VPC comes back v4-only with no error to tell you. Use the switch.
    </Warning>
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/vpcs
    { "name": "prod", "cidr_v4": "10.0.0.0/16", "assign_ipv6_cidr": true }
    ```
  </Tab>
</Tabs>

`assign_ipv6_cidr` delegates a globally-routable `/60` from the region's pool
and returns it as the VPC's `cidr_v6`. Subnets then carve `/64`s out of it.

<Warning>
  This is the **only** way a VPC gets IPv6, and it only works at create. You
  cannot bring your own v6 prefix, and you cannot add one to an existing VPC —
  a v4-only VPC stays v4-only for its whole life. Decide before you create it.
</Warning>

If the region has no IPv6 pool the create fails with `400` and
"IPv6 is not available in this region". Check [regions](/regions) for what is
enabled where.

There is no address translation for IPv6 anywhere on the platform: an instance
in a dual-stack subnet uses its own global address. That changes how outbound-only
IPv6 is expressed — see
[egress-only gateways](/networking/gateways#egress-only-gateways-the-ipv6-case).

### Deleting a VPC

The delete refuses in two cases, each naming what is holding it:

* **Subnets remain.** "VPC has subnets; delete them first."
* **An internet gateway is still attached.** "VPC has an internet gateway
  attached; detach it first."

## Subnets

<Tabs>
  <Tab title="Console">
    Go to **Networking → Subnets** and choose **Create Subnet**. Under
    **Subnet details**, pick the **VPC**, then fill in **Name** and **CIDR**.
    **Gateway IP** is optional and defaults for you.

    Under **Routing**, **Route Table** starts on **main (default)**. This is
    the one field worth deciding here rather than later — see
    [public and private are routing](#public-and-private-are-routing-not-a-flag).
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/subnets
    {
      "vpc_id": "5f8d3a2e-1c4b-4e7a-9f6d-2b1a8c3e5d7f",
      "name": "prod-web",
      "cidr": "10.0.1.0/24"
    }
    ```
  </Tab>
</Tabs>

The CIDR has to sit fully inside the parent VPC's CIDR of the same family, and
must not overlap another subnet in the same VPC. `name` is unique per VPC.

`gateway_ip` defaults to the first address after the network address
(`10.0.1.1` on a `10.0.1.0/24`). You can supply your own, as long as it is
inside the CIDR.

`cidr` and `gateway_ip` are immutable. `description`, `tags` and
`route_table_id` are not.

### Public and private are routing, not a flag

There is no `public` boolean on a subnet. A subnet is public when the route
table it uses has a `0.0.0.0/0` route pointing at an internet gateway, and
private otherwise. The subnet's `route_table_id` is what decides, so moving a
subnet between the two postures is a `PATCH` that re-associates it with a
different table:

```bash theme={null}
PATCH /v1/subnets/{subnet_id}
{ "route_table_id": "a3c9e1f4-7b2d-4a6e-8c1f-9d3b5e7a2c4f" }
```

<Note>
  Re-associating an existing subnet is **API only**. The console picks the
  **Route Table** on **Create Subnet**, and the subnet's own page shows
  **Route table** as a link you can follow but not change. Choose the table at
  create, or move the subnet with this `PATCH`.
</Note>

Everything about which routes exist lives on the
[gateways and routing](/networking/gateways) page.

### Which addresses you actually get

Three addresses in an IPv4 subnet are unavailable: the network address, the
broadcast address, and the gateway. A `/24` leaves 253 usable — nothing is held
back for DNS or instance metadata, because the metadata endpoint is the
link-local `169.254.169.254` rather than an address inside your subnet.

Auto-allocation takes the lowest free address above the gateway. If you supply
`ip_address` on an interface it is checked against all of it: inside the CIDR,
not the network or broadcast address, and not already taken — either by another
interface or held by another resource, such as a load balancer's VIP.

### Dual-stack subnets

Pass `cidr_v6` at create — a `/64` inside the VPC's `cidr_v6`. The VPC must have
been created with `assign_ipv6_cidr`. Omit it for a v4-only subnet.

<Note>
  A dual-stack subnet is **API only**. **Create Subnet** in the console has no
  IPv6 field, so a subnet made there is v4-only even inside a VPC that has a
  `cidr_v6`. The subnet's page does show **IPv6** once one exists.
</Note>

`cidr_v6` on the response is how a client tells the two apart: it is null on a
v4-only subnet and set on a dual-stack one, alongside `gateway_ip_v6`.
Interfaces in a dual-stack subnet get a `/128` automatically.

<Warning>
  A v6 address on the subnet and a `/128` on the NIC do **not** make anything
  reachable. IPv6 reachability is decided entirely by the route table's `::/0`
  route — see
  [IPv6 reachability is the route](/networking/gateways#ipv6-reachability-is-the-route-not-the-address).
</Warning>

### Deleting a subnet

Three things block it, and the error names the one that did:

| Blocker                                | What the error says                                     |
| -------------------------------------- | ------------------------------------------------------- |
| Interfaces still in the subnet         | "subnet still has *N* interface(s) — delete them first" |
| A NAT gateway lives here               | "subnet hosts NAT gateway *name* — delete it first"     |
| An address is held by another resource | names the address and the holder                        |

The third one is the surprising one. A load balancer's VIP is held inside your
subnet without being an interface. The delete refuses rather than dropping the
address underneath the resource that owns it.

## Interfaces

An interface is a NIC: one MAC and one IPv4 address in a subnet, a `/128` too
if the subnet is dual-stack, plus its own
[security-group membership](/networking/security-groups).

<Tabs>
  <Tab title="Console">
    Go to **Networking → Interfaces** and choose **Create Interface**. Under
    **Interface**, pick the **VPC** and **Subnet** and give it a **Name**.
    Leave **IP Address** and **MAC Address** under **Addressing** blank to
    have them assigned.

    A new interface belongs to no security group, which means it drops
    everything. Attach one from the interface's own page before you expect
    traffic — see [security groups](/networking/security-groups).
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/interfaces
    { "subnet_id": "7a1c9d3e-2f5b-4c8a-9e6d-3b2a1c4f5e8d", "name": "web-eth0" }
    ```
  </Tab>
</Tabs>

`ip_address` and `mac` are picked for you when omitted. `name` is unique per
subnet, the IP is unique per subnet, and the MAC is unique. Subnet, IP and MAC
are all immutable — only `description` and `tags` can be patched.

An interface exists on its own. It is not a child of an instance, and it keeps
its address, its MAC and its security groups whether or not anything is
currently using it.

### Attaching an interface to an instance

Attachment happens on the [compute](/compute) side, not here:

<Tabs>
  <Tab title="Console">
    Open the instance under **Compute → Instances** and choose **Attach NIC**.
    The dialog offers the same two paths:

    * **Existing interface** — "Attach a standalone interface you already
      created." Pick it under **Interface**; nothing else is editable,
      because "The interface keeps its address, MAC, and security groups."
    * **New interface** — "Create a NIC on a subnet, attached to this
      instance." Pick a **VPC** and **Subnet**, and optionally set a **Fixed
      IP address** and **Security groups**.

    Confirm with **Attach interface**. The instance's **Networking** tab lists
    what is attached.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/instances/{instance_id}/nics
    { "interface_id": "7a1c9d3e-2f5b-4c8a-9e6d-3b2a1c4f5e8d" }
    ```
  </Tab>
</Tabs>

An interface you created brings its own address, MAC and security groups, so
per-NIC overrides on that call are rejected rather than silently ignored. You
can also pass `subnet_id` instead and let compute provision a NIC for you.

The difference shows up at detach:

<Tabs>
  <Tab title="Interface you created">
    Detaching returns it to standalone. The interface, its address and its
    security-group membership all survive, ready to attach somewhere else.
  </Tab>

  <Tab title="NIC compute provisioned">
    Detaching tears it down. Compute destroys the NICs it created; the address
    goes back to the subnet.
  </Tab>
</Tabs>

<Warning>
  `DELETE /v1/interfaces/{interface_id}` refuses while a floating IP is attached
  to the interface — detach the address first. It does **not** check whether an
  instance is currently using the NIC. Detach it at the instance before you
  delete it, or you remove the interface out from under a live binding.
</Warning>

<Note>
  The `attached_to` field on an interface is always null today. Do not read it
  to decide whether a NIC is in use — list the instance's NICs on the compute
  API instead.
</Note>

## Names, CRNs and tag-conditioned access

Every resource here takes a `tags` map, and every one of them is authorized by
CRN, so [policies](/iam/policies) can name them precisely.

VPCs, subnets, route tables, interfaces, security groups and all three gateway
types get **name-based** CRNs, which is what makes a wildcard readable:

```
crn:network:sa-saopaulo-1:my-account:vpc/prod-*
```

Routes and floating IPs have no name of their own, so their CRNs are keyed by
id. A route additionally authorizes against its **parent route table's** CRN and
tags — the narrowest thing a policy can name — so you grant `network:CreateRoute`
on a table rather than on `*`.

<Warning>
  A CRN is account-scoped, but subnet names are unique per **VPC** and interface
  names per **subnet**. So `subnet/web` in a policy matches a subnet called
  `web` in *every* VPC in the account, not just the one you had in mind. When
  you need to fence a single VPC's subnets, condition on a tag with
  `basalt:ResourceTag/<key>` instead of relying on the name.
</Warning>

## Quotas

VPCs, subnets, interfaces, route tables, security groups and floating IPs are
each capped by a regional quota against your organization. Public IPv4 is the
one worth watching: floating IPs and NAT gateway addresses draw on the **same**
allowance, so a NAT gateway costs you a floating IP's worth of quota.

## Next

<CardGroup cols={2}>
  <Card title="Gateways and routing" icon="route" href="/networking/gateways">
    Getting traffic in and out, and the four things a public instance needs.
  </Card>

  <Card title="Security groups" icon="shield" href="/networking/security-groups">
    Stateful allow rules, the default-deny posture, and what the platform
    enforces regardless.
  </Card>
</CardGroup>
