Repository navigation
Wire Format Reference
This page is the authoritative reference for the ARGUS/1.0 text protocol -- the verbs, status codes, header names, body encoding, and correlation semantics that ArgusTransfer transmits over a named pipe. For a narrative overview and the request flow through the framework, see Architecture and Protocol.
All values listed here are sourced verbatim from the production code (ArgusTransfer/Protocol/). If the table on this page disagrees with the source, the source wins -- please file an issue.
Every request begins with a single line:
{VERB} {ROUTE} ARGUS/1.0\r\n
-
{VERB}is one of the values in the Verbs table below (uppercase, case-sensitive on the wire). -
{ROUTE}may include a query string (?key=value&key2=value2); query parameters are also surfaced separately onArgusRequest.QueryParameters. - The line has exactly three space-separated parts; anything else is rejected (
400 Bad Requestfrom the host). - The third part must be exactly
ARGUS/1.0(case-sensitive). Any other version token -- a non-Argus client such asHTTP/1.1, or a different protocol version -- is rejected with400 Bad Requestand the detailUnsupported protocol version: '...'; expected 'ARGUS/1.0'.There is no version negotiation.
The route is percent-encoded on the wire: in the path, %, spaces and control characters (including CR and LF) are encoded, e.g. /items/a b is sent as /items/a%20b, and the server decodes them again, so ArgusRequest.Route on the server is exactly the route the client set. An encoded slash (%2F) is never decoded, so it cannot act as a path separator. Query parameter keys and values are URL-encoded; in a query written directly into the route string (e.g. client.GetAsync("/items?page=1")) only spaces and control characters are encoded, because % there is taken to be an existing escape.
Mixed versions: a client of version 1.3.0 or later sends
%in a route as%25, which servers up to 1.2.1 do not decode. Routes with spaces or control characters never worked with those servers.
Every response begins with a single line:
ARGUS/1.0 {Code} {ReasonPhrase}\r\n
- The first part must be exactly
ARGUS/1.0(case-sensitive); the client rejects any other version token with aFormatException, which the default retry policy does not retry. -
{Code}is the numeric value of anArgusStatusCode. -
{ReasonPhrase}is the canonical phrase fromArgusStatusCodeExtensions.ToReasonPhrase()-- see the Status Codes table.
The standard header names are exposed as public const string values on ArgusHeaderNames. Header lines follow the request or status line, terminated by a blank line. Header names are case-insensitive. The request or status line plus all header lines may not exceed the reader's MaxHeaderSize (32 KB by default; ArgusPipeHostOptions.MaxRequestHeaderSize on the host); a larger header block raises ArgusProtocolException, which the host answers with 400 Bad Request.
| Header | Constant | Purpose |
|---|---|---|
X-Correlation-Token |
ArgusHeaderNames.CorrelationToken |
A Guid that uniquely identifies the request/response exchange. The router automatically stamps the request's token on the response. |
X-Timestamp |
ArgusHeaderNames.Timestamp |
ISO 8601 timestamp of when the message was created. |
Content-Type |
ArgusHeaderNames.ContentType |
MIME type of the body (e.g., text/plain, application/json, application/problem+json). |
Content-Length |
ArgusHeaderNames.ContentLength |
Size of the body in bytes (UTF-8 encoded, after compression) when the body is sent inline (not chunked). |
Accept |
ArgusHeaderNames.Accept |
The acceptable response content types: a comma-separated list of media ranges (type/subtype, type/*, */*) with optional quality values (;q=0.5). Not sent by default (any type). The server returns 406 NotAcceptable only if no registered serializer is acceptable. See Accept header rules. |
Authorization |
ArgusHeaderNames.Authorization |
The client's credentials as {scheme} {parameter}, e.g. Bearer token123. See Authentication. |
Content-Encoding |
ArgusHeaderNames.ContentEncoding |
The encoding the body is compressed with, e.g. gzip. See Compression. |
Accept-Encoding |
ArgusHeaderNames.AcceptEncoding |
The encodings the client accepts for the response body, e.g. gzip; q=0 excludes an encoding and * accepts any. |
A message that carries a streamed body sends Transfer-Encoding: chunked instead of Content-Length. See Body Encoding below.
Custom headers are supported -- write any key into ArgusRequest.Headers or ArgusResponse.Headers and the wire serializers transmit them verbatim. Header names must be tokens of visible ASCII characters without :, and header values must not contain carriage returns, line feeds or NUL characters. The serializers reject a message that violates this with InvalidOperationException naming the header, so a value taken from untrusted input cannot inject additional headers; the host answers a handler response with such a header with 500 Internal Server Error. ArgusRequest.Authorization and SetAuthorization already reject line breaks when the value is set.
The ArgusVerb enum defines the six supported verbs:
| Verb | Purpose |
|---|---|
GET |
Retrieve a resource or collection of resources. |
POST |
Create a new resource. |
PUT |
Replace an existing resource. |
PATCH |
Partially update an existing resource. |
HEAD |
Retrieve metadata about a resource without the body. |
DELETE |
Delete an existing resource. |
The router automatically strips the body from the response of a successful HEAD request after the handler executes.
The ArgusStatusCode enum defines thirteen status codes. The reason phrase column is exactly what the server emits on the wire (and what ArgusRequestException.ReasonPhrase will carry on the client).
| Code | Name | Reason Phrase | When to use |
|---|---|---|---|
| 200 | Ok |
OK |
Request succeeded; the body (if any) is the resource. |
| 201 | Created |
Created |
A new resource was created; the body is typically the new resource. |
| 204 | NoContent |
No Content |
Request succeeded; intentionally no body. |
| 400 | BadRequest |
Bad Request |
The request was malformed or contained invalid data. |
| 401 | Unauthorized |
Unauthorized |
The request lacks valid authentication credentials. |
| 403 | Forbidden |
Forbidden |
The authenticated caller lacks permission for the requested resource. |
| 404 | NotFound |
Not Found |
The requested resource or route was not found. The router returns this when no template matches. |
| 406 | NotAcceptable |
Not Acceptable |
The server cannot produce a response matching the Accept header. |
| 409 | Conflict |
Conflict |
The request conflicts with the current state of the resource. |
| 422 | UnprocessableEntity |
Unprocessable Entity |
Syntactically valid but the body failed semantic validation. |
| 500 | InternalServerError |
Internal Server Error |
An unexpected error occurred on the server. |
| 501 | NotImplemented |
Not Implemented |
The route matched but the verb is not registered. The router returns this automatically. |
| 503 | ServiceUnavailable |
Service Unavailable |
The server is temporarily unable to handle the request. |
ArgusStatusCodeExtensions.TryParse(int code, out ArgusStatusCode statusCode) parses a numeric code back into the enum.
A message carries its payload either inline with Content-Length or streamed with Transfer-Encoding: chunked -- never both.
The header block is UTF-8 text with \r\n line endings (readers also accept \n). The body is binary: Content-Length and chunk sizes count bytes, and the client and pipe host read and write bodies as raw bytes, so non-ASCII text and arbitrary binary data round-trip exactly. A string body is encoded as UTF-8. A body that ends before its announced length (for example because the connection closed) raises EndOfStreamException.
Content-Length: {byteCount}
Content-Type: text/plain
{body}
This is the default path used when ArgusRequest.Body / ArgusResponse.Body is set to a string. The IArgusBodySerializer registered for the Content-Type is invoked to read or write the string.
When BodyStream is set instead of Body, the wire serializer emits:
Transfer-Encoding: chunked
Content-Type: application/octet-stream
{hex-chunk-size}\r\n
{chunk-data}\r\n
...
0\r\n
\r\n
{hex-chunk-size} is the number of bytes in the chunk; chunk data is raw bytes followed by \r\n. The default chunk size is 8 KiB. The reader validates the chunk-size line as a non-negative hexadecimal number, throwing FormatException for malformed sizes or chunk data that is not followed by \r\n. A configurable maxBodySize cap throws InvalidOperationException if the cumulative chunked body exceeds the limit, and large chunks are read in bounded pieces. These checks defend against denial-of-service via a hostile sender.
When a message has a Content-Encoding header, the body is compressed with that encoding before framing: Content-Length announces the compressed size, and a streamed body's chunks carry compressed bytes. The receiver decompresses the body and removes the Content-Encoding header. See Compression.
POST /reports ARGUS/1.0
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.0000000Z
Accept-Encoding: gzip
Content-Encoding: gzip
Content-Type: text/plain
Content-Length: 318
{318 bytes of gzip data}
The server may respond before it has read the whole request: when the request line and headers exceed MaxRequestHeaderSize, when Content-Length exceeds MaxRequestBodySize, or when a chunked body grows beyond it, the host answers 400 Bad Request right away and closes the connection without reading the rest. A client must therefore read the response while it is still writing the request: the host's pipe is unbuffered on Windows, so a client that finishes writing before it starts reading blocks on its write while the host waits to deliver its response.
ArgusClient does this automatically: it reads the response concurrently with writing the request and cancels the remaining write when an early response arrives. Custom clients built on ArgusRequestSerializer / ArgusResponseSerializer should do the same.
Every ArgusRequest is created with a fresh Guid in CorrelationToken. The router stamps that same token onto the ArgusResponse after the handler completes, so a client can correlate responses to their originating requests. The token is also surfaced on ArgusContext.CorrelationToken for handlers and middleware that want to log or thread it through.
Request:
POST /sampleitems ARGUS/1.0
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.0000000Z
Content-Type: application/json
Content-Length: 42
{"name":"Widget","description":"A widget"}
Response:
ARGUS/1.0 201 Created
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.1234567Z
Content-Type: application/json
Content-Length: 89
{"id":"f7e6d5c4-b3a2-1098-7654-321fedcba098","name":"Widget","description":"A widget"}
- Architecture and Protocol -- request flow, message model, narrative overview
- Serialization -- body serializers and content-type negotiation
- Error Handling -- what happens when a status code is non-success
copyright @ Sam Gerené