Highport orbital control

Editing, deleting and moving on

Four ways to stop, and they undo four different things. Orbital Control will do any of them; picking the wrong one is the most common way to lose something here.

Everything on this page is something you do to a site that already exists. Changing it, taking it down, giving the address up, taking a copy with you. They are four separate acts with four separate sets of consequences, and only one of them is irreversible.

What you want What to do Reversible
Change what the page shows Edit the site and publish again Publish again
Take one file off the site Remove it in the editor and publish Publish again
Take the page down, keep the address Delete the site content Yes. Republish, with nothing to re-verify
Give the address up Release the registration No
Take a copy of everything Highport holds Download everything on /advanced Nothing is changed by it
Make Highport re-read a record it got wrong Publish the record again —

Editing a published site

There is no separate update operation. A site is one record in your account, filed under its hostname, and changing the site means writing that record again at the same address. The Content tab at https://hub.highport.space/sites/{domain} is the same screen you used to publish it the first time, and everything that happens after you press Publish is the same.

Opening the editor loads the record your account currently holds and fills the form in from it. It loads once, and it fills in only the side your record uses. A tile-bound record leaves the file table empty, and a record with its own files leaves the tile unchosen. Switching between the two afterwards keeps whatever you have put on each side, so you can look at one and go back to the other.

What you can change

Part of the site Control Carried over from the published record
Whether the page is a tile or your own files The three source options, plus the tile picker Yes
A tile's parameters The parameters form Yes, re-derived from that tile's declaration
Which file answers which path The file table Yes
Content type and encoding per file The two advanced columns Yes
Redirects The redirects block Yes
Your not-found page and its status code The file input and the 404 / 200 radio Yes
/.well-known/atproto-did and RASL Two checkboxes Yes

Five fields the editor does not carry

The record permits five things the editor has no control for. It never reads them on load and never writes them on publish, so a record carrying any of them loses it the first time you press Publish in the editor. The site record is where each one is defined.

Field What it does What happens
access.requireAuth The path prefixes that ask a visitor to sign in Dropped. The site stops being gated at the next activation
source.name A name on an uploaded bundle Dropped
resources[*].status A per-path status code of 200, 404 or 410 Dropped
notFound.contentType A content type on the not-found page Dropped
notFound.contentEncoding A pre-compressed not-found page Dropped

Only the first of these is likely to cost you something you notice. If your site asks visitors to sign in, make the edit with the same tool that wrote the record. The editor has no control that puts the gating back, so once it is dropped it can only be written again from outside. Asking visitors to sign in is where that field is covered.

If the editor says it could not load your page

Could not load this page means the read of your current record failed. The form still works, and that is the hazard: it is empty, and publishing from it replaces the live record with whatever you compose. Reload the page and wait for the record to appear before you change anything.

There is also no draft and no warning when you navigate away. A half-composed edit is lost when you leave the page.

Files that fall out of the manifest

Removing a file from the manifest removes it from the site and nothing else.

Highport keeps its copy of the bytes. Your account no longer references the upload, so your account's own storage may collect it on its own schedule. That is your provider's business and not Highport's, and Highport's copy is the durable one either way.

Files that did not change are not re-uploaded and not re-fetched. Two sites naming the same bytes share one stored copy. Republishing a site where one file changed is close to free, and an untouched file keeps the same ETag, so a returning visitor's browser gets a 304 and downloads nothing.

Is the site down while it republishes?

No, and not for an instant. Before the swap the domain serves the old page; after the swap it serves the new one. The two happen in one database transaction, so there is no moment at which a request can see half a site.

Reaching every server that answers for your domain takes a little longer: usually a second or two, and up to about two minutes on a domain under continuous traffic. The busy domain being the slow one is the counter-intuitive part. A domain nobody has visited recently has no cached answer to serve from, so it changes over at once, and a domain under constant requests always has one. On top of that, a reader who already loaded the old page keeps it in their own browser for up to 60 seconds. How your site reaches people has the mechanism and the numbers behind it.

One thing about the progress panel at the head of the Content tab. On an edit it goes back to reading Live almost at once, showing the revision that is still serving, with the new one listed underneath as a preparation. That is correct, not stuck. The page keeps polling until the new revision lands, which is what You can close this page at the foot of the tab means: come back whenever, and it picks the progress up wherever it has got to.

Taking a site down

Taking a site down takes the page off the domain and leaves everything else in place.

In the Hub it is the first row of Handing it back, at the foot of the Settings tab of /sites/{domain}, and it is only offered when something is published. The row is titled Take the site down, and its help text is the promise in full:

Visitors stop seeing your page. The domain stays registered to you, the DNS records keep working and the certificate survives, so publishing again brings it straight back with nothing to re-verify.
      

Handing it back, at the foot of a site's Settings tab: Take the site down above Release the registration

The confirmation is a press. It used to ask you to type the hostname, the same as releasing does, and that was the wrong shape for it: this one is undone by publishing again, and asking for the same ceremony as the action that cannot be undone teaches you to type your hostname without reading. Only releasing the domain asks now.

There is no Highport method for deleting a site. The record is yours, it lives in your account, and deleting it is a write to your own account that Highport observes. Any AT Protocol client can do it:

curl -sS -X POST "https://pds.example/xrpc/com.atproto.repo.deleteRecord" \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
              "repo": "did:plc:zqcffbaxaz5ltrz6i2nnfzmf",
              "collection": "space.highport.sites.site",
              "rkey": "example.com"
            }'
      

The account and hostname above are examples. The record key is the hostname, so the delete names its target directly.

If the site is published from a space

The Delete button does not take down a site published from a space. It always deletes from your own account, and a site published from a space does not keep its record there. So the record the call names is not the record your domain is serving, and the site goes on serving.

Delete the record at the space host instead, then ask Highport to pull the space so it sees the change. Spaces has both calls. Releasing the registration also stops the page, in one step and from the Hub, but it gives the name up, the irreversible half of this page, and it leaves the record sitting in the space unused.

When the page stops answering

The delete reaches Highport over the network's event stream, usually within seconds, and the domain is switched off the moment it arrives. How the station notices is what happens in between.

The switch is not the whole story. A domain under continuous traffic can go on answering with the deleted page for up to about two minutes while the caches in front of it expire. A domain nobody is requesting stops immediately. It is the same window a publish goes through, in the other direction.

After that, every path on the domain answers 404 with the page that says Nothing is published here yet. It is one page for four different situations, being never registered, released, suspended, and registered-but-nothing-published, and it does not distinguish them, deliberately.

If the delete never reaches Highport, nothing else notices. Highport will not read a record's absence as a deletion, because an account that cannot be reached looks exactly the same from here, and reading it that way would take live sites down during an outage. A site still serving well after a delete comes down by publishing something else at the address, or by releasing the domain.

What survives a delete

Everything except the page.

Thing Survives
The domain registration Yes. It moves from active back to verified
The _bard.<domain> ownership TXT record Yes. Highport cannot edit anyone's DNS
Your traffic record Yes
Verification state Yes, untouched
The TLS certificate Yes, and the domain stays authorized at the permission endpoint, so a renewal still goes through
The record itself, in your account No. You deleted it
Highport's copy of every file Yes
The built copy of the site Highport was serving Yes, marked as replaced
Traffic and billing history Yes, untouched

Two things the Hub shows afterwards read like faults and are not. /sites/{domain} reports the state as Replaced by a newer version, which is wording written for a different situation, and nothing replaced it. And the page keeps offering Visit site and Delete the site content, because what it reads to decide that is still there.

The domain stays in your dashboard and in space.highport.manage.listDomains, with no active revision. It leaves the public directory: space.highport.sites.resolveDomain and space.highport.sites.getSite answer 404 SiteNotFound, and it is absent from space.highport.sites.listSites.

Republishing after a delete

Publish a record at the same hostname and the site comes back. Nothing is re-verified, because the delete touched no verification state. The ownership record was never invalidated and the certificate was never deauthorized.

A byte-identical republish works. The record's content hash is unchanged but the write is a new one, and Highport orders publishes by the write, not by the content.

The success message the Hub shows after a delete says the same thing in fewer words:

Deleted. Your page stops serving in a moment. The domain, its DNS records and its certificate are untouched, so you can publish something new at the same address whenever you like.
      

Deleting does not delete your files

Nothing on the delete path deletes a byte. The only thing here that ever deletes one is the collector that reclaims unreferenced bytes, and because an account collects blobs no record of its own references any more, the copy the collector is looking at can be the last one anywhere. So it is deliberately conservative. It marks first, waits out a grace period of 30 days by default, re-checks every reference, and only then deletes.

Two things follow, and they are worth knowing before you plan around them. The collector is off by default on a deployment: it observes and reports without deleting until an operator turns it on. And a deleted site's stored snapshot goes on referencing every file it named, so those bytes are not reclaimed by that pass at all. Treat deletion as "the page stops being served", not as "the bytes are gone".

If a file must not exist anywhere, the deployment's operators are who to ask. The abuse contact in the footer is the route.

Releasing a domain

Releasing gives the name up. It is the one action on this page that cannot be undone.

In the Hub it is the second row of Handing it back, titled Release the registration, and it is always offered, because a domain you claimed and never published to has nothing else worth doing to it. It is the row with the hazard striping down its edge — the two are not the same kind of action and the page says so before you read a word of it. Its help text, when something is published:

The domain stops serving and the name becomes available to anybody. Your page itself is untouched and stays with your account, but Highport will not serve it at this address again without a new registration and a fresh verification.
      

The confirmation is typing the hostname exactly; the submit button stays disabled until what you typed matches the domain, character for character. The dialog is titled Release this domain?, its body says the name becomes available to anybody and that it cannot be undone from here, and it says in as many words that your page is not deleted and stays with your account. On success the Hub returns you to the dashboard, instead of leaving you on a page of registration panels and DNS instructions for a domain that is no longer yours. The dashboard says example.com is released. The name is free for anybody to claim.

By hand, it is one call:

POST /xrpc/space.highport.manage.releaseDomain HTTP/1.1
      Authorization: Bearer <service-auth token, lxm = space.highport.manage.releaseDomain>
      Content-Type: application/json

      {"domain": "example.com"}
      
{ "released": true }
      

Every effect, in order

  1. The hostname is canonicalized. A malformed one is InvalidDomain.
  2. Your registration is checked as it stands at that moment. If you are not the holder, or the domain is not currently claimed, including one already released, the answer is DomainNotFound. That one error covers both cases, so the method cannot be used to find out who holds a name.
  3. If anything was serving, it stops. In the same operation as the release, with no grace period, because somebody releasing a domain in a hurry usually wants the content off it now. The caches in front of it take the same couple of minutes a delete takes.
  4. The registration is marked released, and the record of your verification and every certificate field are cleared. Clearing the certificate failure counters matters: they are what Highport reads when it decides whether to authorize a certificate, and leaving them would make it refuse the next holder over a cooldown you earned.
  5. A released entry goes into the domain's event history, with a deactivated entry before it if something was serving.
  6. The change is announced to the servers that answer for the domain, and each one drops what it is holding for it in memory.

The domain then stops being authorized for certificates. Highport's permission endpoint answers 403 for it, though it answers from a cache before it reads the registration, so for up to 30 seconds after the release a request for a certificate can still be allowed. The certificate already issued is neither revoked nor deleted by this call. It stays in the edge's certificate storage until it expires. Nothing is served behind it, because the domain no longer has a site to serve.

The domain leaves your dashboard and the public directory. The crawlable copy of /explore/sites is re-rendered at most once every five minutes, so a released domain can persist in that one server-rendered listing for a few minutes after it is gone from everywhere else.

What survives a release

Registering the name again, by you or by anybody else, starts from scratch: a new registration, and a fresh verification of the ownership record.

An unverified claim releases itself. If you register a domain and never finish verifying it, it is released automatically after 168 hours and the name is free again. Bringing your domain alongside has that clock.

The distinction

Release when you're finished with the domain. Delete when you're finished with the site.

Delete the site content Release the registration
What is written Your record, in your account The registration, in Highport's index
The domain registration Survives Freed for anybody to claim
DNS Untouched Untouched, and now pointless
Certificate Untouched, still authorized Not revoked, no longer authorized
Coming back Publish again, no re-verification Register again, verify again
Offered when Only when something is published Always
Reversible Yes No

Downloading everything

The first panel on /advanced has one button, Download everything, and it is a plain link to GET /api/export. Nothing is changed by pressing it.

The archive is a ZIP, and it is stored rather than compressed. Almost everything large in it is an image or a font that would not shrink, and spending the CPU to not shrink them is a cost paid on every export for nothing. The traffic files under analytics/ are the exception and are compressed, because columns of timestamps and counts are the one thing in there that shrinks. It is built in full before the download starts, so a large site means a wait and then a fast transfer. There is no progress percentage, because the response has no declared length.

The file is named for the moment you asked, to the millisecond: highport-export-2026-09-05T14-32-07.123Z.zip.

What is in it

registrations.json
      analytics/
        manifest.json
        {domain}/traffic-hourly.csv
        {domain}/status-mix.csv
      public/
        {your account}/space.highport.sites.site/{domain}/{content hash}.json
        {your account}/space.highport.sites.tile/{record key}/{content hash}.json
        blobs/{content hash}
      spaces/
        {space type}/{space key}/
          ... the same three, for a space you are the authority for
      

registrations.json is a pretty-printed array of every domain your account has registered, with domain, status, and space where the domain belongs to a space. It includes every status, so a domain you released last year is in your export and is not in your dashboard.

analytics/ is the one part of the archive you could not fetch again from your own account. Everything else came from your PDS and is still there; the traffic numbers were measured here, and Highport deletes them on a schedule. There are two CSV files per registered domain, covering the last 90 days, with a manifest.json beside them naming the exact window the files were read over.

traffic-hourly.csv has one row per hour that saw traffic — hour_utc,requests,bytes_body,bytes_billed — and an hour nobody visited has no row rather than a row of zeroes. status-mix.csv is one row per HTTP status over the whole window — status,requests,bytes_billed,bot_requests — where a 402 row is what your allowance refused. Billed bytes include the per-response header estimate, so they do not match a byte count of the files themselves. A domain nobody visited gets both files with their header lines and nothing else: the file being there is how you tell "no traffic" from "not measured".

bot_requests is the share of a status that came from a crawler, as classified from each request's user agent — the edge says what is kept and for how long. Requests recorded before September 2026 were never classified and count as zero here.

Two things are deliberately missing from it. Nothing in the archive names a visitor. Highport will tell you who authenticated to one of your domains, one domain at a time, on that site's Traffic view — an archive spanning every domain you hold is not that, and it would outlive the retention those rows are kept under. And there are no top paths and no per-version split, because those are answerable for 30 days rather than 90; putting them in a file labelled 90 days would be putting a shorter answer under a longer heading.

Each record is one JSON file, exactly as Highport indexed it, with no wrapper: the identifying facts are in the path. That is not always what your account holds today, and a difference between the two is one of the things the archive is for. There is one file per site, holding the last revision indexed, and one file per version of each tile, which is why a tile you have republished ten times has ten files side by side.

blobs/ holds the files themselves, each named for its content hash with no extension. Nothing in the ZIP's own structure says which of them is your home page. That map is inside the record, at source.resources, where each key is a request path and each value names the file under blobs/. Copy each one to its key and the files are back where they belong. The redirects, the not-found page and any sign-in gating are fields of the record rather than files, so they come back by reading it.

One case will not reconstruct, and it is worth knowing before you rely on the archive. A site bound to a tile somebody else published brings no files with it. The record names the tile by address and content hash, and that tile's bytes belong to its author's account, not yours. Using a tile on your site explains that binding. What lands in the ZIP is five lines of JSON. If you need those files, fetch the tile record from its author's account, or take the bytes off the served site.

A file Highport no longer holds is skipped silently. The record still names its path, so a naive reconstruction produces a site with one broken asset and no error.

What it does not contain

Four things are read to build the archive: your registrations, your site records, your tile records, and 90 days of hourly traffic to your domains. Everything else has no representation in it.

Not in the archive Why
The built copies of your sites Derived from the record and the files, both of which are in there
Registration history, verification and certificate state Not part of the record set
Indexing job history Same
Records you validated but never published Same
Space credentials, keys, grants and receipts Same
Who visited your sites Answered one domain at a time, on that site's Traffic view, and never across the domains you hold
Traffic older than 90 days, top paths, per-version splits The first is outside the export's window; the other two are only answerable for 30 days
Your own browsing of other people's sites A separate read: space.highport.manage.getMyActivity
Consumption history and what you were billed A separate read: space.highport.manage.getConsumption
Billing periods and per-domain caps Not part of the record set
Your session, OAuth tokens and keys Credentials. Never exportable

Highport is not the authority for most of this. The records are your account's, and the files came from there too. An export is not a backup of anything unique — except the traffic numbers, which nothing but Highport ever held. It is the answer to "what does this service actually have about me", which is a different and more useful question.

Calling it by hand

There is no XRPC method and no token path. The export is authenticated by the Hub session cookie and nothing else, and the subject is always the signed-in account. There is no parameter for whose data to export, and no operator method that exports somebody else's.

curl -sS \
        --cookie 'bard_session=<value copied from a signed-in browser>' \
        -o export.zip \
        https://hub.highport.space/api/export
      

No CSRF token is needed, because a GET writes nothing.

Condition Status Error
No session cookie, or an expired one 401 AuthRequired
Too many requests from one session 429 RateLimitExceeded, with Retry-After
Anything failing during the build 500 Internal

Because the archive is finished before the response starts, a failure during the build is a clean error status. There is no half-written ZIP wearing a 200.

Asking for a re-index

"Re-index" names several different things here, and the one people reach for is the one that does the least. Read this section before you wait on one.

What it is Who can ask What it actually does
space.highport.manage.reindex on a site address You, for your own record Nothing. It answers with a job id, and the job is dropped as a repeat of the revision Highport already has
space.highport.manage.reindex on a tile address You, for your own tile Re-runs every tile stage. This one works
Re-read from my PDS on /advanced You Only offered for domains Highport has no registration for, so it answers RecordNotFound
The nightly reconciliation pass Nobody. It runs itself Compares your account's record against Highport's copy and re-runs only when they differ
A rebuild of your account after it moves to a new host Nobody. The network sends it Re-runs every site that account has indexed
space.highport.admin.rebuildSnapshot An operator Nothing, for the same reason as the first row

The button on /advanced says my PDS. That is the account holding your records, the one the rest of this page calls your account.

The first row is the one to be clear about. A site re-index reports success and rebuilds nothing. It answers 200 with a job id, the job settles as done with no error, space.highport.manage.getIndexStatus reports exactly what it reported before, and an operator's view of the job queue shows it completing normally. Nothing about the outcome distinguishes it from a rebuild that ran, so do not wait on one, and do not read a clean job as evidence that something was repaired.

It is rate limited at 30 calls per 60 seconds per account, and the refusal says so: reindex is limited to 30 per 60 seconds per identity.

What actually re-runs a site

Publishing the record again. Any change, or none at all: write the same JSON back and the new write is enough. Through the Hub that is the ordinary flow, so open the editor, press Get it ready, then Publish.

If what changed is the record in your account rather than Highport's copy of it, the nightly pass finds it on its own, within 24 hours by default, without you asking.

For a site published from a space, the Hub's re-index route always names your own account's copy of the record, so it answers RecordNotFound. Spaces has the sync call that applies instead.

When you would actually need one

Two situations exist, both from bugs that shipped and were fixed, and both affecting sites indexed before the fix.

A tile-bound site recorded as if it served its own files. Nothing served wrong. The page, its bytes and /_bard/site.json were always correct. What is wrong is one line in Highport's index, and the symptom is two screens away: the tile's page in the directory says no site is using it while your site is serving it, and space.highport.sites.listSites?tile= does not match your domain. The nightly pass cannot repair it, because the record is not what is wrong. Republish the site. Any edit, or none.

A tile whose icon was never fetched. The icon is a field beside the tile's files rather than one of them, and it was not being collected, so space.highport.sites.getTileIcon answers 404 TileNotFound while the directory advertises an icon URL. The symptom is a broken image on the tile's card. This is the one case a re-index fixes, because it is a tile:

curl -sS -X POST \
        -H 'Authorization: Bearer <service-auth token, lxm = space.highport.manage.reindex>' \
        -H 'Content-Type: application/json' \
        -d '{"uri":"at://did:plc:z72i7hdynmk6r22z27h6tvur/space.highport.sites.tile/3lqk4bqzfhs2k"}' \
        https://highport.space/xrpc/space.highport.manage.reindex
      

The account and record key above are examples. The caller must be the tile's own account, there is no control for it in the Hub, and republishing the tile does the same job.

How to tell it worked

space.highport.manage.getIndexStatus for the domain, and one field in it:

{
        "state": "active",
        "rev": "3lrevaaaaaa2z",
        "cid": "bafkreia4w54blzdgvovgbdc7baqhnp5aohygjqt36g6ak5inwiidzlkno4",
        "entryCount": 12,
        "fetched": 12,
        "pending": 0,
        "failed": [],
        "errors": [],
        "activatedAt": "2026-08-23T12:00:00.000Z"
      }
      

The revision, hash and timestamp above are examples; the field set is exact. activatedAt moves when a new build of the site goes live, and only then. If it holds its old value, nothing was rebuilt. For the tile-binding case specifically, the check is the directory: your domain now appears on the tile's page, and space.highport.sites.listSites?tile= matches it.

Site owners · 11 of 12