Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Stdlib

The standard library — and where each call runs

The pure standard library, and the one fact about it that changes how your app feels: 126 of its 155 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.

stable2 examples compiled by CIstdlibclientserverperformance

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. 126 of the 155 stdlib methods can. So

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. You do not opt in, and there is no annotation. Write the code; the compiler works out where it can run.

Where each call runs#

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#

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.

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.)

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#

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?#

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.

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(value) runs in the browser for a scalar or a list of scalars. Hand it a class or an entity and it goes to the server: naming and ordering the members requires the model, which the browser does not hold. Guessing at a member order would produce JSON that parses and is wrong.

Does this 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 3, away from zero, 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:

// 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");
}
[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"));

  // AWAY FROM ZERO, not banker's rounding — `Math.Round(2.5)` is 3. A client that used JS's Math.round
  // would agree here and disagree on 3.5, which is how a half-cent walks into an invoice.
  Assert.Equal(3m, 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#

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.

103 run on the client · 9 are server-only · 2 decide per call site.

Clock

MethodRuns onWhy not the client
Backoff.Capserverbounds a retry policy, which only the durable engine reads
Backoff.Exponentialservera retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one
Backoff.Fixedservera retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one
Backoff.Jitterserverbounds a retry policy, which only the durable engine reads
Backoff.Linearservera retry policy is read only by the durable engine, which schedules the retry — a client holds nothing that acts on one
Backoff.MaxAttemptsserverbounds a retry policy, which only the durable engine reads
Convert.AmbientAppFieldclientships as an appField node — the client-safe app identity (Slug / Domain / BaseUrl) from the boot app ambient (the internal carrier of App.Slug/Domain/BaseUrl)
Convert.AmbientCultureclientships as an ambientCulture / cultureFormatAmbient node — the viewer's culture token from the metadata payload (the internal carrier of Session.CurrentCulture)
Convert.AmbientUserFieldclientships as a currentUserField node — a scalar of the viewer's principal from the boot bag (the internal carrier of Session.CurrentUser fields)
Convert.FromBase64Stringclient
Convert.ToBase64Stringclient
Convert.ToBoolclient
Convert.ToDecimal/1client
Convert.ToDecimal/2conditionalgated per call site — a literal declared culture ships as a cultureParse node; a dynamic culture stays server
Convert.ToDoubleclient
Convert.ToInt/1client
Convert.ToInt/2conditionalgated per call site — a literal declared culture ships as a cultureParse (int profile) node; a dynamic culture stays server
Convert.ToInt64client
Convert.ToString/1client
Convert.ToString/2conditionalgated per call site — a literal standard specifier or a plannable custom pattern over a Decimal/Int32 ships; a runtime format, a Double, or an unplannable pattern stays server
Convert.ToString/3conditionalgated 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
Crypto.Decryptserverdecryption uses the app's platform-managed KMS key, which the client never holds
Crypto.Encryptserverencryption uses the app's platform-managed KMS key, which the client never holds
Crypto.FixedTimeEqualsservera constant-time compare is only meaningful next to the secret it guards
Crypto.HmacSha256Hexserveran HMAC is keyed; the key is the host's and must not reach the browser
Crypto.Md5Hexclient
Crypto.Sha256Hexclient
Date.AddDaysclient
Date.AddHoursclient
Date.AddMinutesclient
Date.AddMonthsclient
Date.AddYearsclient
Date.Dateclient
Date.Dayclient
Date.DayOfWeekclient
Date.Hourclient
Date.Minuteclient
Date.Monthclient
Date.Secondclient
Date.Yearclient
DateOnly.FromDateTimeclient
DateOnly.Newclient
DateOnly.Parseclient
DateTime.MaxValueclient
DateTime.MinValueclient
DateTime.Newclient
DateTime.Parseclient
DateTime.ParseExactconditionalgated per call site — a literal custom yyyy-pattern + a literal declared culture ships as a cultureDateParse node; a standard specifier / 2-digit year / dynamic pattern|culture stays server
DateTime.Todayclient
DateTime.UtcNowclient
DateTimeOffset.UtcNowclient
DurableClock.Nowclient
DurableClock.Todayclient
DurableClock.UtcNowclient
Enum.Descriptionclientmodel-backed; ships as std:Enum.Description, dispatched by the client's dedicated arm
Enum.Labelclientmodel-backed; ships as std:Enum.Label, dispatched by the client's dedicated arm over the enum map
Enum.Nameclientmodel-backed; ships as std:Enum.Name, dispatched by the client's dedicated arm
Enumerable.Rangeclient
File.SignedUrlserverneeds the session's app + the signing key to mint a signed URL — never leaves the server
File.Urlclientships as fileUrl — the render evaluator builds the URL in-process
Guid.Emptyclient
Guid.NewGuidclient
Guid.Parseclient
JsonSerializer.Serializeconditionalgated by CanJsonSerializeOnClient — a scalar or list of scalars ships; a class or entity needs the server's model
Math.Absclient
Math.Acosclient
Math.Asinclient
Math.Atanclient
Math.Atan2clienttakes (y, x) — C#'s order. Atan(y / x) is not the same: it loses the sign and folds two quadrants onto two others
Math.Ceilclient
Math.Ceilingclient
Math.Clampclient
Math.Cosclientradians
Math.Expclient
Math.Floorclient
Math.Logclient
Math.Log10client
Math.Log2client
Math.Maxclient
Math.Minclient
Math.Powclient
Math.Roundclient
Math.Signclient
Math.Sinclientradians
Math.Sqrtclient
Math.Tanclientradians
Math.Truncateclient
Regex.IsMatchclientships as regexIsMatch, with the pattern translated to JS's dialect at compile time
Regex.Replaceclientships as regexReplace, with the pattern translated to JS's dialect at compile time
Regex.Splitclientships as regexSplit, with the pattern translated to JS's dialect at compile time
Security.HashPasswordserverpassword hashing runs where the host's cost factor and salt RNG live
Security.IssueJwtservera JWT is signed with the host's key; a client-minted token would be self-issued authority
Security.LinkOAuthFromPendingserverit opens a server-sealed pending-oauth token and writes the identity link; the verified subject never crosses to the client
Security.RandomHexserveran unguessable value must come from the host's RNG, not the user's machine
Security.RandomIdserveran unguessable id must come from the host's RNG, not the user's machine
Security.VerifyPasswordserverpassword verification runs where the hash does — the client never sees a hash
Security.VerifyPendingOAuthEmailserverit opens a server-sealed pending-oauth token; the callback-verified email re-enters only inside the seal, never as a client argument
Text.ByteSizeclient
Text.Capitalizeclient
Text.Concatclient
Text.Containsclient
Text.EndsWithclient
Text.IndexOfclient
Text.IsBlankclient
Text.IsEmptyclient
Text.Joinclient
Text.LastIndexOfclient
Text.Leftclient
Text.Lengthclient
Text.Likeclient
Text.Lowerclient
Text.PadEndclient
Text.PadStartclient
Text.Repeatclient
Text.Replaceclient
Text.Reverseclient
Text.Rightclient
Text.Splitclient
Text.StartsWithclient
Text.Substringclient
Text.TitleCaseclient
Text.Trimclient
Text.TrimEndclient
Text.TrimStartclient
Text.Truncateclient
Text.Upperclient
TimeOnly.FromDateTimeclient
TimeOnly.Newclient
TimeOnly.Parseclient
TimeSpan.Daysclient
TimeSpan.FromDaysclient
TimeSpan.FromHoursclient
TimeSpan.FromMillisecondsclient
TimeSpan.FromMinutesclient
TimeSpan.FromSecondsclient
TimeSpan.Hoursclient
TimeSpan.Minutesclient
TimeSpan.Newclient
TimeSpan.Parseclient
TimeSpan.Secondsclient
TimeSpan.TotalDaysclient
TimeSpan.TotalHoursclient
TimeSpan.TotalMillisecondsclient
TimeSpan.TotalMinutesclient
TimeSpan.TotalSecondsclient
TimeSpan.Zeroclient
Uri.EscapeDataStringclient
Uri.UnescapeDataStringclient
WebUtility.HtmlDecodeclient
WebUtility.HtmlEncodeclient
Zone.InZoneconditionalships for a LITERAL declared zone (client has its DST plan); a dynamic zone → server
Zone.IsDstconditionalships for a LITERAL declared zone; a dynamic zone → server
Zone.Newserverthe zone factory is a compile-time literal fold — no runtime work to accelerate
Zone.OffsetAtconditionalships for a LITERAL declared zone; a dynamic zone → server
Zone.Resolveconditionalgated per call site — a literal (declared) zone ships as a zoneResolve node; a dynamic zone stays server
string.Concatclient
string.Joinclient

See also#

Related

Regex

Match, replace, split and CAPTURE with regular expressions — the C#-faithful System.Text.RegularExpressions spelling…

Security.* — hashing, verifying, tickets, random ids

The calls an authentication flow needs: `HashPassword` (salted, one-way), `VerifyPassword` (constant-work comparison…

Encoding — Base64, URL, HTML

Encode and decode text — Base64, URL percent-encoding, and HTML escaping — with the C#-faithful spellings…

Uri

Parse a URL into its parts with the C#-faithful `new Uri(url)` handle. Construct it from a URL string, then read…