Skip to content

Networking

The network is slow, partial, optional, and untrustworthy. A good API admits all four facts.

This page is about talking to the network from a d/BASIC program. After reading it you will be able to fetch text or bytes from a web address in one call, keep a longer session alive from a window program without freezing it, check which kinds of networking the machine actually offers, and handle passwords without ever putting them in a string.

Networking in d/BASIC is a service the operating system provides, not a socket you drive. Your program names a destination and a kind of session; the d/OS runtime, the program that runs your compiled code, owns Wi-Fi, DNS, sockets, TLS, proxies, radios and the rules for what is allowed. The same compiled program can reach an HTTPS server through the browser's fetch, a desktop TLS stack, the cartridge that carries a modern processor into the 8-bit machine, a FujiNet, or built-in networking. When no route exists, it is told so plainly.

The language never hands you a native socket pointer. Progress comes in bounded steps, the session belongs to your program, and "nothing has arrived yet" is a normal state, not an error.

Begin with d/Works, not a socket

IMPORT dworks.network

d/Works is the office suite being written in d/BASIC (d/Write, d/Calc, d/Present), and its network library gives beginners a complete HTTP request in one call and gives interactive programs a session they can poll. Both rest on the same network intents (an intent is a named request, such as "open this session", that the machine carries out with whatever network it has). Neither exposes a radio, a TLS engine, a native descriptor, a browser request object or a memory address.

If a program is useless offline, say so in its application header:

APP "WEATHER"
  LABEL "Weather"
  NEEDS NETWORK
END APP

The runtime can then decline to open the application at all, instead of showing a dashboard that can never fill in. If networking is optional, say a notes app that can sync but also works locally, ask what is available and keep the offline path real.

Fetch text in one understandable operation

IMPORT dworks.network

DIM Reply AS WorksHttpResponse
DIM Text AS STRING

Reply = NEW WorksHttpResponse()
ON REFUSAL GOTO FetchFailed

WorksHttpGetText "https://example.com/hello.txt", 4096, Text, Reply

IF Reply.IsSuccessStatus() THEN
  PRINT Text
ELSE
  PRINT "THE SERVER ANSWERED HTTP "; Reply.StatusCode
END IF
END

FetchFailed:
PRINT "COULD NOT FETCH. RULE "; ERR
END

WorksHttpGetText may pause this program's task while d/OS, the desktop and other applications carry on. It fills in Text and Reply only once the response is complete, so you never see a half-updated result.

Reaching the server and getting a good answer are different things. A 404 or 503 is a completed HTTP exchange, so no network error is raised; IsSuccessStatus() is true only for 200 through 299.

The response object

Member Meaning
StatusCode HTTP response code
BytesWritten bytes actually delivered to your string or array
TotalBytesKnown nonzero when TotalBytes is a length the server actually stated
TotalBytes the complete response length when known, before any truncation
Truncated nonzero when the limit you gave could not hold the body
ContentType the response media type, bounded in length
RedirectLocation the redirect destination, when the rules allow it to be returned
IsSuccessStatus() -1 only for a 2xx response

The maximum byte count is part of the call, not a hidden global. Truncation is loud. A program that needs every byte checks Truncated; it compares BytesWritten with TotalBytes only when TotalBytesKnown is nonzero. An unknown length is never confused with an empty response.

Binary bodies go into a bounded INTEGER array

WorksHttpGet writes the response into a one-dimensional INTEGER array. Each element holds two little-endian bytes, the same convention every d/Works library uses for raw bytes:

IMPORT dworks.network

DIM Body%(0 TO 2047)
DIM Reply AS WorksHttpResponse

Reply = NEW WorksHttpResponse()
WorksHttpGet "https://example.com/icon.bin", Body%(), Reply

IF Reply.IsSuccessStatus() AND Reply.Truncated = 0 THEN
  PRINT "RECEIVED "; Reply.BytesWritten; " BYTES"
ELSE
  PRINT "HTTP "; Reply.StatusCode; " OR INCOMPLETE BODY"
END IF
END

There is no ADR(Body%(0)) spelling. The runtime borrows the whole array for one call, checks its dimensions and capacity, and cannot keep a pointer into d/BASIC memory afterwards.

Interactive work uses a session

A window program should not sit inside a one-shot fetch while the person expects menus, cancellation and progress. Instead it owns a WorksNetworkSession object and moves it forward in bounded turns:

d/Works operation Job
WorksSessionOpen(Request, Session) check the request and begin a session
WorksSessionPoll(Session, Event) advance once and report Pending, HostKey, Ready, or Closed
WorksSessionRead(Session, Chunk%(), Count&, EndOfStream%) read a partial bounded chunk and report end of stream separately
WorksSessionWrite(Session, Chunk%(), Offered&, Written&) offer a bounded chunk and report how many bytes were taken
WorksSessionResize(Session, Columns%, Rows%) resize an interactive terminal session
WorksSessionDecideHostKey(Session, Decision) answer an explicit SSH trust question
WorksSessionCancel(Session) ask for the session to be cancelled
WorksSessionClose(Session) release the session and the host's state; safe to call more than once

The session object is what your source owns; the runtime keeps the version-checked network state outside any d/BASIC field. No handle can be printed, put in a DOBJ (the typed object a program hands to another program when you drag, drop, copy or paste), copied into a TYPE, or mistaken for an address.

The shorthand arguments above are typed d/Works values: Request is a WorksNetworkRequest, Event a WorksNetworkEvent, Caps a WorksNetworkCaps, Status a WorksWifiStatus, Results a WorksWifiResults, and Decision a WorksHostKeyDecision. The owning value is WorksNetworkSession; these helper objects expose named properties and constructors while keeping their fixed byte records private.

A GUI controller's state

Keep these ordinary values in the application's model:

  • phase: closed, opening, host-key decision, ready, body, complete or failed;
  • bytes delivered and any unsent remainder of a short write;
  • a deadline on the monotonic clock (the one that only moves forward);
  • protocol status and the detail of the last error;
  • whether the user asked to cancel; and
  • enough state to show an honest progress message.

Call WorksSessionPoll once per timer, frame or completion turn. When progress changes what is on screen, call SERVICE INVALIDATE() to ask for a repaint. Never loop and wait inside WINDOW SUB DRAW(); drawing paints the current model and returns. In WINDOW SUB CLOSE(), cancel and close even if the request looks finished.

No progress is not failure

Network reads and writes may be partial. A session event can stay Pending, a read can deliver zero bytes before the end of the stream, and a write can take fewer bytes than you offered. Therefore:

  • keep and retry the part of a write that was not taken;
  • tell "no progress" apart from "end of stream";
  • yield instead of spinning;
  • measure timeouts with the runtime's monotonic clock;
  • keep cancel and close reachable from every state; and
  • expect a stale session, or one owned by someone else, to be declined by its version number.

Cancel and close are safe to repeat. An OK, connection-closed or stale-handle result hands ownership back; any other final result leaves ownership with you so a later close can try again.

This discipline matters on a 6502, the processor in the Atari 800, the Commodore 64 and the Apple II, but it is just as valuable on a desktop: a slow DNS server should never freeze a window.

Capabilities are granular

WorksNetworkCapabilities(Caps) reports separate support for each of:

Capability What it permits
Status inspect the state of the network service
Routes inspect the network routes that are allowed
Wi-Fi inspect or scan the wireless state the system owns
NTP synchronize or inspect network time
Connections take a bounded snapshot of current connections
DNS resolve names
TCP open a raw byte stream where the host's rules permit
Ping test reachability without pretending it proves an application protocol works
HTTP / HTTPS request response streams; HTTPS includes an approved TLS route
Telnet / SSH interactive terminal sessions
FTP / SFTP / SCP file-transfer sessions and the supervised mounts they publish

Never assume HTTPS from HTTP, SSH from TCP, or SFTP from SSH. A host can honestly offer any subset. Default ports are HTTP 80, HTTPS 443, Telnet 23, SSH 22, SFTP 22, SCP 22, and FTP 21; an explicit destination may choose another.

WorksWifiStatus(Status) reads the current link state. WorksWifiScan(Results) takes a bounded snapshot of nearby networks. Joining a network goes through d/OS's own secure system dialog: passwords are never ordinary d/BASIC STRING values passed through an app.

APP ... NEEDS NETWORK is the coarse promise checked when the program loads; WorksNetworkCapabilities is the live, detailed answer. A network service can go away after the program starts, so every operation still has a final-result or error path. Hardware being plugged in does not, by itself, prove a capability works.

The low-level wrapper layer

Most programs should stop at the friendly operations above. Library authors can use the exact one-dimensional integer-array wrappers:

  • DworksNetworkQueryCaps
  • DworksNetworkSessionOpen
  • DworksNetworkSessionPoll
  • DworksNetworkHttpResponse
  • DworksNetworkSessionRead / DworksNetworkSessionWrite
  • DworksNetworkSessionResizeTerminal
  • DworksNetworkSessionHostKeyDecision
  • DworksNetworkSessionCancel / DworksNetworkSessionClose

They pack and check the request and reply arrays around the registered SERVICE.NETWORK_* intents. These are asynchronous operating-system requests, not a separate dialect of the language. Packing their arrays by hand in application code is allowed but rarely kind to the next reader.

Download, validate, then reveal

Networking often supplies an image, a sound, a document or a package. Never paint, play or install the part that has arrived so far. Download into private staging, require a successful protocol status and a complete body, check the format, declared length, content type and digest as appropriate, then reveal the finished object in one step.

If any step fails, the old document or resource stays visible and unchanged. This is the network version of the load-once rule for resources: users should not have to wonder whether half an image became real.

Transfers become files only after success

FTP, SFTP and SCP sessions may, once ready, appear in the file namespace as /ftp0, /sftp0 or /scp0. That appearance is a completed handover, not something Open promises. A failed, cancelled or half-signed-in session publishes nothing.

Keeping partial remote state out of ordinary file browsing lets file manipulation use the same rules for local and remote storage.

Credentials and trust

Secrets do not belong in ordinary URL strings, command history, diagnostics, DOBJ payloads or logs. Credentials travel through the runtime's secret channel; temporary copies are erased after use.

Important protocol truths stay visible:

  • FTP is plaintext, kept for compatibility; it is not secure transfer.
  • HTTPS checks its TLS peer against the trust policy installed on the machine.
  • SSH, SFTP and SCP require explicit trust in the host key. A first unknown key can go through a deliberate trust-on-first-use or pinning step; a changed key is rejected rather than silently replaced.
  • For SSH, SFTP and SCP, submitting an empty credential selects the id_ed25519 key the network service holds; a non-empty one is a password. SFTP and SCP still require a user name.
  • An FTP request with only a host is anonymous. Authenticated FTP supplies a user name and may deliberately use an empty password.
  • None of these forms means "borrow whatever the browser or desktop has lying around".

How each runner adapts

Where it runs Route and boundary
browser / WebAssembly HTTP(S) may go through fetch; CORS and page-origin rules still apply. Raw TCP is not offered, the browser's cookies are never borrowed, and this route always uses credentials: "omit". A browser request that carries credentials needs a separately offered, supervised network service.
Windows / Linux / macOS system DNS, sockets and TLS can raise concurrency and buffer ceilings while keeping the same partial-transfer, timeout, status and cancellation behaviour.
d/OS with a network service built-in Wi-Fi, the cartridge coprocessor, a FujiNet or a host bridge may do the expensive DNS and TLS work and expose the same bounded session.
d/OS without one the capability is absent, and the program (or the open) is declined by name. Offline operation continues when networking was optional.

A permission-denied result is policy, not a reason to retry furiously. Pending or no-progress is temporary. An unsupported result says to pick a route that actually exists. Application code handles all of these through ON REFUSAL, ERR and the normal session result objects; it does not depend on undeclared magic constants.

A runtime may shrink chunk sizes and concurrency to fit the machine, but it never lowers security: HTTPS does not become HTTP, and SFTP does not become FTP.

A network program's checklist

  1. Declare that networking is required, or keep a complete offline mode.
  2. Query the exact capability you will use.
  3. Keep credentials out of destinations and logs.
  4. Poll and transfer in bounded turns.
  5. Treat zero progress and partial progress as normal.
  6. Check both whether the transfer worked and what the protocol said.
  7. Compare total and delivered bytes when completeness matters.
  8. Cancel and close on success, on error, on timeout and when the window closes.

Next: Intents & Services →