# The standard library — and where each call runs

> The pure standard library, and the one fact about it that changes how your app feels: 271 of its 396 methods run in the BROWSER, with no round trip. The rest that do not are held there by ONE thing — they need a secret the browser must never hold (a signing key, a KMS key, a password hash). Nothing here needs a capability.

<!-- id: stdlib-index · area: stdlib · stability: stable · html: https://osysharp.com/reference/stdlib/index/ -->

## Summary        {#summary}
The standard library is the set of **pure** calls available in any function body — text, numbers, dates, encoding,
hashing. No `use`, no capability, no ceremony.

The thing worth knowing about it is **where each call runs**. Osy# decides that for you: a function whose body the
browser can execute runs *in the browser*, and never makes a network call. **271 of the 396 stdlib methods can.** So

```osy syntax
var label = Text.Upper(name);              // runs in the browser
var price = Convert.ToString(total, "F2"); // runs in the browser
var due   = Date.AddDays(start, 30);       // runs in the browser
```

…are all free. Write the code; the compiler works out where it can
run.

## Where each call runs        {#description}
**The default is the client, and the goal is that it always is.** A method is kept on the server only when running it in
the browser would give a *different or unsafe answer* — never because it was hard to port. There is one reason that is a
true floor, and a shrinking frontier of methods on their way to the client.

### 1 — It needs a secret the browser must never hold        {#host-authority}
This is the floor — a handful of `Security.*` and `Crypto.*` operations, and the only ones that can *never* move. The
client is the **user's own machine**, so the JWT signing key, the KMS encryption key, an HMAC key and a password
hash's cost/salt are precisely the things it must not see. Running these in the browser would not be slow — it would
either **leak the secret** or let the client **forge authority** (a client-minted token is self-issued). The round
trip is the security boundary, not a performance cost.

```osy title="the handful that can never leave the server" syntax
Security.HashPassword(pw)     // server — the cost factor and the salt RNG are the host's
Security.IssueJwt(claims)     // server — signed with the host's key; a client could forge one
Crypto.Encrypt(secret)        // server — the KMS key never reaches the browser
```

Host-authoritative randomness sits here too: `Security.RandomId` / `RandomHex` must come from the host's RNG, or an
"unguessable" id is guessable.

The clock is **not** one of these, and it is worth saying so directly because it looks like it should be. `DurableClock.Now`,
`DurableClock.UtcNow` and `DateTime.UtcNow` **run on the client.** A `DateTime` here is a UTC *instant*, not a wall-clock
reading, so a browser and the server name the same value — 9am in Frankfurt *is* 5pm in Tokyo, the same instant — and
reading it in the browser costs no round trip. (Test pinning still works, and a resumed durable function still sees a
consistent instant, because this engine resumes from a saved point rather than re-running your function from the top.)

```osy title="the clock is NOT one of them — it reads in the browser" syntax
var now  = DurableClock.Now;                        // client — a UTC instant, read in the browser
var due  = Date.AddDays(order.Placed, 30);   // client — pure arithmetic on a value in hand
var year = Date.Year(order.Placed);          // client
```

### 2 — The browser cannot yet reproduce it faithfully        {#fidelity}
This has shrunk to essentially one case, and it is a specific fidelity gap, not a law of nature: a `Double` handed to
`Convert.ToString(value, format)` still renders on the server, because .NET produces its digits from the value's exact
*binary* expansion. (`Decimal` — this platform's money type, and what a format specifier is almost always for — already
formats in the browser.)

Everything that used to sit here has moved to the client: `WebUtility.HtmlDecode` now ports .NET's *own* algorithm and
entity table (not the browser's, which decodes a different set — it accepts `&copy` *without* the semicolon where .NET
does not); `WebUtility.HtmlEncode` ships its five-character escape set; and the unkeyed hashes `Crypto.Sha256Hex` /
`Crypto.Md5Hex` run in the browser too, since neither holds a secret.

## Which two calls decide per call site?        {#conditional}
Two methods are not client-or-server by *name* — the **arguments** decide, so the same method may run in the browser at
one call site and on the server at the next.

**`Convert.ToString(value, format)`** — and the `$"{total:F2}"` that lowers onto it. It runs in the browser when the
format is a *literal* — either a *standard* specifier (`F2`, `N0`, `C`, `P1`) or a *custom* digit pattern
(`"#,##0.00"`, `"0.00"`, `"00000"`) — over a `Decimal` or `Int32`. A format built at *runtime*, or a `Double`, still
goes to the server.

```osy syntax
var a = Convert.ToString(total, "F2");        // client — literal, standard, decimal
var b = Convert.ToString(total, "#,##0.00");  // client — the compiler parses the pattern into a plan and ships it
var c = Convert.ToString(total, fmt);         // server — a runtime format string the compiler cannot see
```

A *literal* custom pattern (`b`) is a compile-time constant, so the compiler parses it **once** into a plan — how many
digits, whether to group — and ships the plan; the browser executes arithmetic, never a parser (the same trick regular
expressions already use). A pattern that reaches past the digit-placeholder core — section separators, scaling,
percent, scientific, embedded literals — stays on the server, where .NET renders it correctly.

**A v1 boundary worth stating plainly:** client-side custom-pattern formatting works only for a **compile-time literal**
pattern, because the mechanism is the *compiler* parsing it into a plan. A **runtime-built** format string (`c` — where
the format is a variable) the compiler cannot see, so it stays on the server; today translating it would mean shipping
a second .NET-format parser to every browser. That is a v1 limitation, not a permanent one: a future version could ship
a small client-side format *interpreter* so a runtime-built pattern also runs in the browser. Until then, `c` is a
round trip and returns the right string.

**`JsonSerializer.Serialize` and `JsonSerializer.Deserialize<T>`** run in the browser — a scalar, a class, a list, a
dictionary, with or without options — and answer what the server answers ([JsonSerializer](https://osysharp.com/reference/json/serializer/) `#in-a-page`). What the
browser needs of the model (a class's members, in order, under their wire names) is sent with the call. A stored row
is the exception: its members are read on the server, as the person reading.

## Does this change my code?        {#does-it-change-my-code}
**No.** Where a call runs never changes its *answer* — the client arms are pinned against the real server by an oracle
suite, down to the details C# gets opinionated about (`Math.Round(2.5)` is `2`, half to even, on both sides;
`Uri.EscapeDataString` escapes `!'()*`, which `encodeURIComponent` does not).

It changes only how the app *feels*. A form that formats a price, validates a pattern and computes a due date now does
all of it without touching the network. And if you write a function that mixes a client-runnable call with a
server-only one, the function simply runs on the server — the answer is still right, it just costs a round trip.

Here is the surface actually running — and the two answers C# has an opinion about, pinned. The client arms are held
to exactly these, so the assertions below are true **wherever the function ends up running**:

```osy title="a function that never touches the network" test app=stdlib-index
// Every call in this body is client-runnable, so the whole function runs in the browser. No annotation, no opt-in.
string PriceLabel(decimal total) {
  return Text.Upper("total") + ": " + Convert.ToString(total, "F2");
}
```

```osy title="the same answer on either side" run app=stdlib-index
[Test]
void The_stdlib_gives_the_same_answer_wherever_it_runs() {
  Assert.Equal("TOTAL: 12.50", PriceLabel(12.5m));           // two decimals, not "12.5"
  Assert.Equal("ACME", Text.Upper("acme"));

  // HALF TO EVEN, as in C# — `Math.Round(2.5)` is 2 and `Math.Round(3.5)` is 4. A client that used JS's
  // Math.round would answer 3 for the first, which is how a half-cent walks into an invoice.
  Assert.Equal(2m, Math.Round(2.5m));
  Assert.Equal(4m, Math.Round(3.5m));

  // A literal custom pattern — the money-grid case — runs in the browser too: the compiler parses "#,##0.00"
  // into a plan and ships it. Grouped, two decimals, rounded away from zero on the exact digits.
  Assert.Equal("1,234.57", Convert.ToString(1234.5678m, "#,##0.00"));

  // The deterministic date surface: pure arithmetic on a value already in hand. Runs in the browser.
  var due = Date.AddDays(DateTime.Parse("2026-01-01"), 30);
  Assert.Equal(31, Date.Day(due));
  Assert.Equal(2026, Date.Year(due));
}
```

## Every method, and where it runs        {#the-table}
Generated from the compiler's own allowlist and **checked on every build** — if a method moves, is added, or is
removed, this table fails the build until it is right. It cannot go stale.

**235 run on the client · 89 are server-only · 11 decide per call site.**

| Method | Runs on | Why not the client |
|---|---|---|
| `Backoff.Cap` | server | bounds a retry policy, which only the durable engine reads |
| `Backoff.Exponential` | server | a retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one |
| `Backoff.Fixed` | server | a retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one |
| `Backoff.Jitter` | server | bounds a retry policy, which only the durable engine reads |
| `Backoff.Linear` | server | a retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one |
| `Backoff.MaxAttempts` | server | bounds a retry policy, which only the durable engine reads |
| `Bytes.Length` | server | takes `byte[]` — see `Crypto.Sha256` |
| `Bytes.Skip` | server | takes and answers `byte[]` — see `Crypto.Sha256` |
| `Bytes.Slice` | server | takes and answers `byte[]` — see `Crypto.Sha256` |
| `Bytes.Take` | server | takes and answers `byte[]` — see `Crypto.Sha256` |
| `Colors.On` | client |  |
| `Convert.AmbientAppField` | client | ships as an appField node — the client-safe app identity (Slug / Domain / BaseUrl / Version / PlatformVersion) from the boot app ambient (the internal carrier of `App.Slug`/`Domain`/`BaseUrl`/`Version`/`PlatformVersion`) |
| `Convert.AmbientCulture` | client | ships as an ambientCulture / cultureFormatAmbient node — the viewer's culture token from the metadata payload (the internal carrier of Session.CurrentCulture) |
| `Convert.AmbientUserField` | client | ships as a currentUserField node — a scalar of the viewer's principal from the boot bag (the internal carrier of `Session.CurrentUser` fields) |
| `Convert.FromBase64String` | client |  |
| `Convert.FromHexString` | server | answers `byte[]` — see `Crypto.Sha256` |
| `Convert.ToBase64String` | client |  |
| `Convert.ToBool` | client |  |
| `Convert.ToBoolean` | client |  |
| `Convert.ToDecimal/1` | client |  |
| `Convert.ToDecimal/2` | conditional | gated per call site — a literal declared culture ships as a cultureParse node; a dynamic culture stays server |
| `Convert.ToDouble` | client |  |
| `Convert.ToHexString` | server | takes `byte[]` — see `Crypto.Sha256` |
| `Convert.ToHexStringLower` | server | takes `byte[]` — see `Crypto.Sha256` |
| `Convert.ToInt/1` | client |  |
| `Convert.ToInt/2` | conditional | gated per call site — a literal declared culture ships as a cultureParse (int profile) node; a dynamic culture stays server |
| `Convert.ToInt32/1` | client |  |
| `Convert.ToInt32/2` | conditional | same as Convert.ToInt/2 — C#'s spelling of the same call, matched by the same gate. The BASE overload `Convert.ToInt32(s, 16)` (an integer second argument) always ships, as a std: call the client's convert arm dispatches on the argument's type |
| `Convert.ToInt64` | client |  |
| `Convert.ToString/1` | client |  |
| `Convert.ToString/2` | conditional | gated per call site — every numeric format ships (any type, any format, literal or runtime); a date format ships only for the supported token set |
| `Convert.ToString/3` | conditional | gated per call site — a literal supported format + a literal (declared) culture over a Decimal/Int32 ships as a cultureFormat node keyed by token; anything else stays server |
| `Cost.AllocatedKbSince` | server | it reads the server process's allocation counter |
| `Cost.Mark` | server | it marks the server's own statement, row and round-trip counters for the current request |
| `Cost.OptimizedBuild` | server | it reads how the server's platform was built |
| `Cost.RequestsSince` | server | it reads the server's count of the requests the test's pages made |
| `Cost.RoundTripsSince` | server | it reads the server's round-trip counter for the current request |
| `Cost.RowsReadSince` | server | it reads the server's row counters for the current request |
| `Cost.RowsWrittenSince` | server | it reads the server's row counters for the current request |
| `Cost.ServerCallsSince` | server | it reads the server's count of the server calls the test's pages made |
| `Cost.StatementsSince` | server | it reads the server's statement counter for the current request |
| `Crypto.Decrypt` | server | decryption uses the app's platform-managed KMS key, which the client never holds |
| `Crypto.Encrypt` | server | encryption uses the app's platform-managed KMS key, which the client never holds |
| `Crypto.FixedTimeEquals` | server | a constant-time compare is only meaningful next to the secret it guards |
| `Crypto.FromHex` | server | answers `byte[]` — see `Crypto.Sha256` |
| `Crypto.HmacSha256` | server | an HMAC is keyed with the host's key, which the client must never hold |
| `Crypto.HmacSha256Hex` | server | an HMAC is keyed; the key is the host's and must not reach the browser |
| `Crypto.Md5Hex` | client |  |
| `Crypto.Sha256` | server | the digest itself is already ported as `Crypto.Sha256Hex`; this overload takes and answers `byte[]`, and the client has no representation of one yet |
| `Crypto.Sha256Hex` | client |  |
| `Crypto.SignEs256` | server | signing uses a PRIVATE key; a key the browser can observe is a key that has leaked |
| `Crypto.SignRs256` | server | signing uses a PRIVATE key; a key the browser can observe is a key that has leaked |
| `Crypto.ToHex` | server | takes `byte[]` — see `Crypto.Sha256`; the conversion is trivial, the value type is what is missing |
| `Crypto.VerifyEs256` | server | verification takes a `byte[]` signature, which the client has no representation of |
| `Crypto.VerifyRs256` | server | verification takes a `byte[]` signature, which the client has no representation of |
| `CultureInfo.InvariantCulture` | client |  |
| `Date.AddDays` | client |  |
| `Date.AddHours` | client |  |
| `Date.AddMicroseconds` | client |  |
| `Date.AddMilliseconds` | client |  |
| `Date.AddMinutes` | client |  |
| `Date.AddMonths` | client |  |
| `Date.AddSeconds` | client |  |
| `Date.AddTicks` | client |  |
| `Date.AddYears` | client |  |
| `Date.Bucket` | server | it reads a zone's clock that is a setting, not a literal the browser holds a plan for — and it is the twin of the SQL a report groups by |
| `Date.Compare` | client |  |
| `Date.Date` | client |  |
| `Date.DateTime` | client |  |
| `Date.Day` | client |  |
| `Date.DayNumber` | client |  |
| `Date.DayOfWeek` | client |  |
| `Date.DayOfYear` | client |  |
| `Date.GetDateTimeFormats/1` | client |  |
| `Date.GetDateTimeFormats/2` | client |  |
| `Date.GetDateTimeFormats/3` | server | a named culture's date patterns — the client holds the invariant culture's table only |
| `Date.GetDateTimeFormatsIn` | server | a named culture's date patterns — the client holds the invariant culture's table only |
| `Date.GetHashCode` | client |  |
| `Date.GetTypeCode` | client |  |
| `Date.Hour` | client |  |
| `Date.Microsecond` | client |  |
| `Date.Millisecond` | client |  |
| `Date.Minute` | client |  |
| `Date.Month` | client |  |
| `Date.Nanosecond` | client |  |
| `Date.Offset` | client |  |
| `Date.Second` | client |  |
| `Date.Ticks` | client |  |
| `Date.TimeOfDay` | client |  |
| `Date.ToDateTime` | client |  |
| `Date.ToTimeSpan` | client |  |
| `Date.ToFileTimeUtc` | client |  |
| `Date.ToOADate` | client |  |
| `Date.ToOffset` | client |  |
| `Date.ToUniversalTime` | client |  |
| `Date.ToUnixTimeMilliseconds` | client |  |
| `Date.ToUnixTimeSeconds` | client |  |
| `Date.UtcDateTime` | client |  |
| `Date.Year` | client |  |
| `DateOnly.FromDateTime` | client |  |
| `DateOnly.FromDayNumber` | client |  |
| `DateOnly.MaxValue` | client |  |
| `DateOnly.MinValue` | client |  |
| `DateOnly.New` | client |  |
| `DateOnly.Parse` | client |  |
| `DateTime.Combine` | client |  |
| `DateTime.Compare` | client |  |
| `DateTime.DaysInMonth` | client |  |
| `DateTime.FromBinary` | client |  |
| `DateTime.FromFileTimeUtc` | client |  |
| `DateTime.FromOADate` | client |  |
| `DateTime.FromTicks` | client |  |
| `DateTime.IsLeapYear` | client |  |
| `DateTime.MaxValue` | client |  |
| `DateTime.MinValue` | client |  |
| `DateTime.New` | client |  |
| `DateTime.NewMs` | client |  |
| `DateTime.NewUs` | client |  |
| `DateTime.Now` | client |  |
| `DateTime.Parse/1` | client |  |
| `DateTime.Parse/2` | server | a named culture's lenient date parse — the client reproduces ParseExact over a culture, not .NET's pattern search |
| `DateTime.Parse/3` | server | a named culture's lenient date parse, with styles — the client reproduces ParseExact over a culture, not .NET's pattern search |
| `DateTime.ParseExact` | conditional | gated per call site — the invariant culture with a formats array ships as a std call; a named culture stays server |
| `DateTime.SpecifyKind` | client |  |
| `DateTime.Today` | client |  |
| `DateTime.UnixEpoch` | client |  |
| `DateTime.UtcNow` | client |  |
| `DateTimeOffset.FromUnixTimeMilliseconds` | client |  |
| `DateTimeOffset.FromUnixTimeSeconds` | client |  |
| `DateTimeOffset.Now` | client |  |
| `DateTimeOffset.Parse` | client |  |
| `DateTimeOffset.UnixEpoch` | client |  |
| `DateTimeOffset.UtcNow` | client |  |
| `DurableClock.Now` | client |  |
| `DurableClock.Today` | client |  |
| `DurableClock.UtcNow` | client |  |
| `Encoding.GetEncoding` | client |  |
| `Enum.Description` | client | model-backed; ships as std:Enum.Description, dispatched by the client's dedicated arm |
| `Enum.Label` | client | model-backed; ships as std:Enum.Label, dispatched by the client's dedicated arm over the enum map |
| `Enum.Name` | client | model-backed; ships as std:Enum.Name, dispatched by the client's dedicated arm |
| `Enumerable.Range` | client |  |
| `Enumerable.Repeat` | client |  |
| `Enumerable.Sequence` | client |  |
| `Environment.GetEnvironmentVariable` | client | lowered to Convert.AmbientEnvironmentVariable, which ships as a configField node |
| `Environment.NewLine` | client | lowered by the resolver to the literal "\n" |
| `File.SignedUrl` | server | a signed URL is a bearer capability sealed with the app's platform-managed key; the client must never be able to mint one, so it is signed on the server and shipped as a value |
| `File.Url` | client | ships as fileUrl — the render evaluator builds the URL in-process |
| `Geography.Parse` | client |  |
| `Geography.Point` | client |  |
| `Guid.Empty` | client |  |
| `Guid.NewGuid` | client |  |
| `Guid.Parse` | client |  |
| `JsonSerializer.Serialize` | conditional | gated by CanJsonSerializeOnClient — everything but a stored row, an interface and an abstract class ships, with its shape; the options form is gated the same way |
| `Math.Abs` | client |  |
| `Math.Acos` | client |  |
| `Math.Asin` | client |  |
| `Math.Atan` | client |  |
| `Math.Atan2` | client |  |
| `Math.Ceil` | client |  |
| `Math.Ceiling` | client |  |
| `Math.Clamp` | client |  |
| `Math.Cos` | client |  |
| `Math.Exp` | client |  |
| `Math.Floor` | client |  |
| `Math.Log` | client |  |
| `Math.Log10` | client |  |
| `Math.Log2` | client |  |
| `Math.Max` | client |  |
| `Math.Min` | client |  |
| `Math.Pow` | client |  |
| `Math.Round` | client |  |
| `Math.Sign` | client |  |
| `Math.Sin` | client |  |
| `Math.Sqrt` | client |  |
| `Math.Tan` | client |  |
| `Math.Truncate` | client |  |
| `Mime.Build` | server | it answers the message's bytes, a `byte[]` the client has no representation of; mail is sent from the server |
| `Mime.Parse` | server | a message's bytes are a `byte[]`, which the client has no representation of; mail is received on the server |
| `Path.AltDirectorySeparatorChar` | client |  |
| `Path.ChangeExtension` | client |  |
| `Path.Combine` | client |  |
| `Path.DirectorySeparatorChar` | client |  |
| `Path.EndsInDirectorySeparator` | client |  |
| `Path.GetDirectoryName` | client |  |
| `Path.GetExtension` | client |  |
| `Path.GetFileName` | client |  |
| `Path.GetFileNameWithoutExtension` | client |  |
| `Path.GetFullPath` | client |  |
| `Path.GetInvalidFileNameChars` | client |  |
| `Path.GetInvalidPathChars` | client |  |
| `Path.GetPathRoot` | client |  |
| `Path.GetRelativePath` | client |  |
| `Path.HasExtension` | client |  |
| `Path.IsPathFullyQualified` | client |  |
| `Path.IsPathRooted` | client |  |
| `Path.Join` | client |  |
| `Path.PathSeparator` | client |  |
| `Path.TrimEndingDirectorySeparator` | client |  |
| `Path.VolumeSeparatorChar` | client |  |
| `Qr.Encode` | server | the encoder is native to the server, where the text a code carries is usually made (a provider's frame derived from a secret); the page is sent the rows |
| `Random.Next` | client |  |
| `Records.FieldName` | server | it reads the field a selector names off the selector's compiled body, which the server holds |
| `Records.IsA` | server | the type hierarchy is read off the model the server holds |
| `Records.Link` | server | a record's link is the page that `[Opens]` it, which the server compiled |
| `Records.MayRead` | server | it evaluates another principal's security rules against a row, which only the server holds |
| `Records.Noun` | server | a record's noun is read off the model the server holds |
| `Records.Readable` | server | it evaluates another principal's security rules against rows, which only the server holds |
| `Records.RollsUpTo` | server | what a record rolls up to was compiled into the server's model |
| `Records.Text` | server | it reads the type of the field a selector names off the selector's compiled body, which the server holds |
| `Records.Time` | server | it reads a field by name off the model the server holds |
| `Records.Title` | server | a record's title is what its `[Title]` declared, which the server compiled |
| `Records.Type` | server | a record's type is read off the model the server holds |
| `Records.Value` | server | it reads a field by name off the model the server holds |
| `Regex.IsMatch` | client | ships as regexIsMatch, with the pattern translated to JS's dialect at compile time |
| `Regex.Replace` | client | ships as regexReplace, with the pattern translated to JS's dialect at compile time |
| `Regex.Split` | client | ships as regexSplit, with the pattern translated to JS's dialect at compile time |
| `Request.ClientAddress` | server | the caller's address is what the server that received the request sees; the browser does not know its own public address |
| `Request.Header` | server | an inbound request header exists only on the server that received it; the browser is the sender, not the receiver |
| `Request.OidcClaim` | server | an inbound bearer token is verified only on the server that received it; the browser is the sender, not the receiver |
| `Request.OidcEmail` | server | an inbound bearer token is verified only on the server that received it; the browser is the sender, not the receiver |
| `Request.Query` | server | an inbound request's query string exists only on the server that received it; the browser is the sender, not the receiver |
| `Request.RawBody` | server | an inbound request body exists only on the server that received it; the browser is the sender, not the receiver |
| `Security.CurrentUserId` | server | who is acting is the server's to say — it is the principal the request was authenticated as, which a browser could only claim |
| `Security.EndOtherSessions` | server | it ends the account's other sessions in the platform's ticket store when the unit of work commits — an act only the server can perform |
| `Security.Fingerprint` | server | a fingerprint is keyed by the app's key, which lives with the host — a browser that could compute one would be holding the key |
| `Security.HashApiKey` | server | an API key is hashed where it is stored and checked — on the server |
| `Security.HashPassword` | server | password hashing runs where the host's cost factor and salt RNG live |
| `Security.IsMasked` | client | store-backed; ships as std:Security.IsMasked, answered by the client's dedicated arm from the withheld-members list each row carries |
| `Security.IsSecretSet` | server | whether a secret is configured is a fact about the server's secret store, which the browser cannot read and should not be told about |
| `Security.IssueJwt` | server | a JWT is signed with the host's key; a client-minted token would be self-issued authority |
| `Security.LinkOAuthFromPending` | server | writing the OAuth identity link is a server-authority write (and the sealed provider subject never reaches the client) |
| `Security.MayRead` | server | it evaluates another principal's security rules against a row, which only the server holds |
| `Security.NewApiKey` | server | an API key must come from the host's RNG, and it is shown once from the server |
| `Security.OAuthConnectUrl` | server | starting a connection seals state with the app's own key and names one of its secrets — the server does it, and the page navigates to the URL it answers |
| `Security.OAuthLinkedProviders` | server | the identity links are the platform's own rows, read on the server |
| `Security.PendingOAuthAccountCreatedAt` | server | the account's age is a provider fact the platform measured and sealed; a client that could open it could claim any age |
| `Security.PendingOAuthLinkedUser` | server | the pending-oauth token is sealed with the app's platform-managed key, and the link it is matched against is the platform's own row; only the server can say which account a provider identity signs in to |
| `Security.PendingOAuthLogin` | server | the pending-oauth token is sealed with the app's platform-managed key; only the server can open it to read the provider's handle for the account |
| `Security.PendingOAuthName` | server | the pending-oauth token is sealed with the app's platform-managed key; only the server can open it to read the name the provider gave |
| `Security.PendingOAuthProvider` | server | the pending-oauth token is sealed with the app's platform-managed key; only the server can open it to say which provider verified this identity |
| `Security.RandomHex` | server | an unguessable value must come from the host's RNG, not the user's machine |
| `Security.RandomId` | server | an unguessable id must come from the host's RNG, not the user's machine |
| `Security.SignInProviders` | server | the declared OAuth clients are the app's configuration on the server; a page asks a server function for them |
| `Security.SignOutEverywhere` | server | it ends sessions, which the server keeps |
| `Security.SignedInElsewhere` | server | it reads the person's other sessions, which the server keeps |
| `Security.UnlinkOAuth` | server | removing an identity link is a server-authority write to the platform's own row |
| `Security.VerifyPassword` | server | password verification runs where the hash does — the client never sees a hash |
| `Security.VerifyPendingOAuthEmail` | server | the pending-oauth token is sealed with the app's platform-managed key; only the server can open it to read the provider-verified email |
| `TestOAuth.PendingToken` | server | it mints the platform-sealed pending-oauth token; the sealing key is the host's, so only the server can produce one |
| `TestOidc.Bearer` | server | it stands in for a bearer token the server verified on an inbound request; only the server receives one |
| `TestOidc.Claim` | server | it stands in for a bearer token the server verified on an inbound request; only the server receives one |
| `Text.ByteSize` | client |  |
| `Text.Capitalize` | client |  |
| `Text.Clone` | client |  |
| `Text.CompareTo` | client |  |
| `Text.Concat` | client |  |
| `Text.Contains` | client |  |
| `Text.EndsWith` | client |  |
| `Text.Equals` | client |  |
| `Text.FromBytes` | server | takes `byte[]` — see `Crypto.Sha256` |
| `Text.FromChars` | client |  |
| `Text.GetHashCode` | client |  |
| `Text.GetTypeCode` | client |  |
| `Text.IndexOf` | client |  |
| `Text.IndexOfAny` | client |  |
| `Text.IndexOfMode` | client |  |
| `Text.Insert` | client |  |
| `Text.IsBlank` | client |  |
| `Text.IsEmpty` | client |  |
| `Text.IsNormalized` | client |  |
| `Text.Join` | client |  |
| `Text.LastIndexOf` | client |  |
| `Text.LastIndexOfAny` | client |  |
| `Text.LastIndexOfMode` | client |  |
| `Text.Left` | client |  |
| `Text.Length` | client |  |
| `Text.Like` | client |  |
| `Text.Lower` | client |  |
| `Text.Normalize` | client |  |
| `Text.PadEnd` | client |  |
| `Text.PadStart` | client |  |
| `Text.Remove` | client |  |
| `Text.Repeat` | client |  |
| `Text.RepeatChar` | client |  |
| `Text.Replace` | client |  |
| `Text.ReplaceLineEndings` | client |  |
| `Text.Reverse` | client |  |
| `Text.Right` | client |  |
| `Text.Split` | client |  |
| `Text.SplitOptions` | client |  |
| `Text.StartsWith` | client |  |
| `Text.Substring` | client |  |
| `Text.TitleCase` | client |  |
| `Text.ToBytes` | server | answers `byte[]` — see `Crypto.Sha256`. It is the usual FIRST call of a byte chain, so its absence is what an author meets first |
| `Text.ToCharArray` | client |  |
| `Text.Trim` | client |  |
| `Text.TrimEnd` | client |  |
| `Text.TrimStart` | client |  |
| `Text.Truncate` | client |  |
| `Text.Upper` | client |  |
| `TimeOnly.FromDateTime` | client |  |
| `TimeOnly.FromTimeSpan` | client |  |
| `TimeOnly.MaxValue` | client |  |
| `TimeOnly.MinValue` | client |  |
| `TimeOnly.New` | client |  |
| `TimeOnly.Parse` | client |  |
| `TimeSpan.Days` | client |  |
| `TimeSpan.FromDays` | client |  |
| `TimeSpan.FromHours` | client |  |
| `TimeSpan.FromMilliseconds` | client |  |
| `TimeSpan.FromMinutes` | client |  |
| `TimeSpan.FromSeconds` | client |  |
| `TimeSpan.FromTicks` | client |  |
| `TimeSpan.Hours` | client |  |
| `TimeSpan.MaxValue` | client |  |
| `TimeSpan.MinValue` | client |  |
| `TimeSpan.Minutes` | client |  |
| `TimeSpan.New` | client |  |
| `TimeSpan.Parse` | client |  |
| `TimeSpan.Seconds` | client |  |
| `TimeSpan.Ticks` | client |  |
| `TimeSpan.TotalDays` | client |  |
| `TimeSpan.TotalHours` | client |  |
| `TimeSpan.TotalMilliseconds` | client |  |
| `TimeSpan.TotalMinutes` | client |  |
| `TimeSpan.TotalSeconds` | client |  |
| `TimeSpan.Zero` | client |  |
| `Totp.Code` | server | a code is made from the secret, which the browser must never hold |
| `Totp.NewSecret` | server | a second factor's secret must come from the host's RNG, not the user's machine |
| `Totp.Uri` | server | the address carries the secret; it is made on the server beside it, and the page is sent the QR rows |
| `Totp.Verify` | server | checking a code needs the secret it proves possession of, which the browser must never hold |
| `Uri.EscapeDataString` | client |  |
| `Uri.SchemeDelimiter` | client |  |
| `Uri.UriSchemeFile` | client |  |
| `Uri.UriSchemeFtp` | client |  |
| `Uri.UriSchemeHttp` | client |  |
| `Uri.UriSchemeHttps` | client |  |
| `Uri.UriSchemeMailto` | client |  |
| `Uri.UriSchemeWs` | client |  |
| `Uri.UriSchemeWss` | client |  |
| `Uri.UnescapeDataString` | client |  |
| `WebAuthn.CreationOptions` | server | the options carry a challenge the server minted and keeps; the page is sent them |
| `WebAuthn.CredentialId` | server | it reads an answer only the server acts on; the page hands the answer over whole |
| `WebAuthn.NewTestKey` | server | the authenticator's half is for tests, which run on the server; a browser has its own authenticator |
| `WebAuthn.Register` | server | a passkey is accepted by the server it will sign in to — a page checking its own answer proves nothing |
| `WebAuthn.RequestOptions` | server | the options carry a challenge the server minted and keeps; the page is sent them |
| `WebAuthn.TestCreate` | server | the authenticator's half is for tests, which run on the server; a browser has its own authenticator |
| `WebAuthn.TestCredentialId` | server | the authenticator's half is for tests, which run on the server; a browser has its own authenticator |
| `WebAuthn.TestGet` | server | the authenticator's half is for tests, which run on the server; a browser has its own authenticator |
| `WebAuthn.Verify` | server | a sign-in is checked by the server it signs in to — a page checking its own answer proves nothing |
| `WebUtility.HtmlDecode` | client |  |
| `WebUtility.HtmlEncode` | client |  |
| `Zip.List` | server | it reads an archive's bytes, a `byte[]` the client has no representation of; a page is sent the listing |
| `TimeZoneInfo.BaseUtcOffset` | conditional | ships as a zoneInfo node — read off the zone's plan, which carries the server's answer |
| `TimeZoneInfo.ConvertTimeFromUtc` | conditional | ships as an inZone node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.ConvertTimeToUtc` | conditional | ships as a zoneToUtc node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.DisplayName` | conditional | ships as a zoneInfo node — read off the zone's plan, which carries the server's answer |
| `TimeZoneInfo.FindSystemTimeZoneById` | client |  |
| `TimeZoneInfo.GetUtcOffset` | conditional | ships as a zoneOffsetAt node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.Id` | client |  |
| `TimeZoneInfo.IsAmbiguousTime` | conditional | ships as a zoneIsAmbiguous node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.IsDaylightSavingTime` | conditional | ships as a zoneIsDst node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.IsInvalidTime` | conditional | ships as a zoneIsInvalid node — a literal zone as its token, a zone value as its expression |
| `TimeZoneInfo.SupportsDaylightSavingTime` | conditional | ships as a zoneInfo node — read off the zone's plan, which carries the server's answer |
| `TimeZoneInfo.Utc` | client |  |
| `bool.Parse` | client |  |
| `char.IsDigit` | client |  |
| `char.IsLetter` | client |  |
| `char.IsLetterOrDigit` | client |  |
| `char.IsLower` | client |  |
| `char.IsPunctuation` | client |  |
| `char.IsUpper` | client |  |
| `char.IsWhiteSpace` | client |  |
| `char.Parse` | client |  |
| `char.ToLower` | client |  |
| `char.ToString` | client |  |
| `char.ToUpper` | client |  |
| `decimal.Parse/1` | client |  |
| `decimal.Parse/2` | conditional | gated per call site — a literal or the viewer's culture ships as a culturePrimitiveParse node; a culture in a variable stays server |
| `double.IsFinite` | client |  |
| `double.IsInfinity` | client |  |
| `double.IsNaN` | client |  |
| `double.IsNegativeInfinity` | client |  |
| `double.IsPositiveInfinity` | client |  |
| `double.Parse/1` | client |  |
| `double.Parse/2` | conditional | gated per call site — a literal or the viewer's culture ships as a culturePrimitiveParse node; a culture in a variable stays server |
| `int.Parse/1` | client |  |
| `int.Parse/2` | conditional | gated per call site — a literal or the viewer's culture ships as a culturePrimitiveParse node; a culture in a variable stays server |
| `long.Parse/1` | client |  |
| `long.Parse/2` | conditional | gated per call site — a literal or the viewer's culture ships as a culturePrimitiveParse node; a culture in a variable stays server |
| `string.Compare` | client |  |
| `string.CompareOrdinal` | client |  |
| `string.Concat` | client |  |
| `string.Equals` | client |  |
| `string.Intern` | client |  |
| `string.IsNullOrEmpty` | client |  |
| `string.IsNullOrWhiteSpace` | client |  |
| `string.Join` | client |  |

## See also        {#see-also}
- <span class="planned" title="this page is planned and not written yet">stdlib-text</span> · [Regex](https://osysharp.com/reference/stdlib/regex/) · [Encoding — Base64, hex, text encodings, URL, HTML](https://osysharp.com/reference/stdlib/encoding/) · [Uri](https://osysharp.com/reference/stdlib/uri/) — the individual surfaces
- [Security.* — hashing, verifying, tickets, random ids](https://osysharp.com/reference/stdlib/security/) — the server-only authority surface, and why it is
