Libraries
The official libraries ship outside the standard library, so a new product version or API
change reaches programs without a compiler release. Each is published by the
admlang publisher, signed, and installed with adm get; the groups are
separate repositories on GitHub. How libraries are declared, signed and published is on the
Libraries docs page. 19 of the 46 libraries below are built; the
rest are marked planned.
adm search adm.database
adm get admlang:adm.database.postgres
Database
Official ADM libraries that connect an ADM program to a database. Each is published as adm.database.<name> and signed by the admlang publisher. They live outside the standard library so that a fix or a new protocol version ships without a std release.
Source: github.com/admlang/database
adm.database.sqlite
SQLite for std.data.db. The engine (SQLite 3.53.4) is compiled into your program from the C source that ships with the library, so there is nothing else to install and no server to run.
adm get admlang:adm.database.sqlite
use (
std.data.db
adm.database.sqlite
)
let conn = try db.connect("sqlite:app.db")
adm.database.postgres
PostgreSQL for std.data.db. The driver speaks the server's wire protocol (version 3.0) itself, in ADM, over std.net: there is no C library to install.
adm get admlang:adm.database.postgres
use (
std.data.db
adm.database.postgres
)
let conn = try db.connect("postgres://app:secret@db.internal:5432/shop?sslmode=verify-full")
adm.database.mysql
MySQL and MariaDB for std.data.db. The driver speaks the client/server protocol itself, in ADM, over std.net: there is no C library to install.
adm get admlang:adm.database.mysql
use (
std.data.db
adm.database.mysql
)
let conn = try db.connect("mysql://app:secret@db.internal:3306/shop?sslmode=verify-full")
adm.database.mongodb
A MongoDB driver in ADM over std.net: the wire protocol itself, BSON from std.data.encoding, replica sets and sharded clusters. It talks to MongoDB 4.2 and later and to the servers that speak its protocol (Amazon DocumentDB, Azure Cosmos DB for MongoDB, FerretDB). MongoDB is not SQL, so this is not a std.data.db driver: it has an API of its own, shaped like the official drivers.
adm get admlang:adm.database.mongodb
use (
adm.database.mongodb
std.data.encoding::(bson)
)
struct Customer {
@bson("_id")
id mongodb.ObjectId
name string
email ?string = none
tags string[]
}
let client = try mongodb.connect("mongodb://app:secret@db.internal/shop?replicaSet=rs0&tls=true")
let customers = client.database("shop").collection<Customer>("customers")
try customers.insert(Customer{name: "Ana", tags: ["new"]}) // the driver makes the _id
let ana = try customers.findOne({"name": "Ana"}) // ?Customer
for let c in try customers.find({"tags": "new"}).sort({"name": 1}).limit(100).cursor() {
print(c.name)
}
try customers.updateOne({"name": "Ana"}, {"$set": {"email": "ana@example.net"}})
try client.close()
adm.database.store
An embedded key-value database: one file, buckets of ordered keys, transactions that are all-or-nothing and survive a crash. It is written in ADM; there is nothing to install and no server to run. For SQL in the same process use [adm.database.sqlite](../sqlite/).
adm get admlang:adm.database.store
use adm.database.store
let db = try store.open("app.db")
try db.write(def (tx store.Transaction) !none {
let users = try tx.createBucket("users")
try users.put("ana", "ana@example.com")
return none
})
try db.read(def (tx store.Transaction) !none {
let users = try tx.bucket("users")
print(try users.text("ana")) unless users is none
return none
})
try db.close()
adm.database.orm planned
A typed data access layer over std.data.db and its drivers: a repository per struct, a query builder that writes each database's SQL with bound parameters, relations, and migrations derived from the structs. It is one opinion about how to use SQL from ADM, so it is a library rather than part of std.data.db, which stays the neutral SQL layer every driver implements.
Not built yet: the example shows the API as designed.
adm get admlang:adm.database.orm
use (
std.data.db
adm.database.orm
adm.database.postgres
)
@orm.table("customers")
struct Customer {
@orm.key(generated = true)
id int
name string
@db.column("mail")
email ?string
@orm.hasMany(by = "customerId")
orders Order[]
@orm.version()
version int
}
let conn = try db.connect("postgres://app:secret@db.internal/shop")
let customers = new orm.Repository<Customer>(conn)
let ana = try customers.find(7) // ?Customer
let recent = try customers.query().where("name", orm.like, "A%").orderBy("name").limit(50).all()
try customers.save(ana) // insert or update, version checked
let withOrders = try customers.query().include("orders").all()
adm.database.elasticsearch planned
An Elasticsearch and OpenSearch client in ADM over std.net.http: documents as typed structs, bulk indexing, search with a typed query builder, aggregations, and index management. It talks to Elasticsearch 8 and OpenSearch 2 and later.
Not built yet: the example shows the API as designed.
adm get admlang:adm.database.elasticsearch
use adm.database.elasticsearch
struct Product {
id string
name string
price float
tags string[]
}
let es = try elasticsearch.connect("https://search.internal:9200", apiKey = key)
let products = es.index<Product>("products")
try products.put("7", {id: "7", name: "Kettle", price: 39.9, tags: ["kitchen"]})
let query = elasticsearch.match("name", "kettle").and(elasticsearch.term("tags", "kitchen"))
let hits = try products.search(query, size = 20, sort = "price")
for let hit in hits {
print("{hit.source.name} {hit.score}")
}
Network
Official ADM libraries that connect an ADM program to infrastructure services. Each is written in ADM over std.net, published as adm.network.<name> and signed by the admlang publisher. They live outside the standard library so that a fix or a new protocol version ships without a std release.
Source: github.com/admlang/network
adm.network.redis
A Redis client. It speaks RESP2 and RESP3 itself, in ADM, over std.net: there is no C library to install. It talks to Redis, Valkey and other servers that speak the same protocol.
adm get admlang:adm.network.redis
use adm.network.redis
let r = try redis.connect("redis://cache.internal:6379/0")
try r.set("greeting", "hello", expire = 1min)
let greeting = try r.get("greeting") // ?string
let hits = try r.incr("hits")
try r.hset("user:7", {"name": "Ana", "visits": 3})
let user = try r.hgetAll("user:7") // map<string, string>
try r.close()
adm.network.kafka
An Apache Kafka client. It speaks the Kafka protocol itself, in ADM, over std.net: there is no C library to install. It talks to Kafka brokers from version 2.4 on and to the services that speak the same protocol (Redpanda, Amazon MSK, Confluent Cloud, Azure Event Hubs).
adm get admlang:adm.network.kafka
use adm.network.kafka
let k = try kafka.connect("broker1:9092", "broker2:9092")
try k.createTopic("orders", partitions = 6)
let out = try k.producer()
let sent = try out.send("orders", "paid", key = "order-7") // Sent{topic, partition, offset}
let orders = try k.consumer("billing", "orders")
for let record in try orders.poll(1s) {
print("{record.partition}/{record.offset}: {kafka.text(record)}")
}
try orders.close()
try out.close()
try k.close()
adm.network.amqp
An AMQP 0-9-1 client. It speaks the protocol itself, in ADM, over std.net: there is no C library to install. It talks to RabbitMQ and to brokers that speak the same protocol (LavinMQ, Apache Qpid).
adm get admlang:adm.network.amqp
use adm.network.amqp
let mq = try amqp.connect("amqp://app:secret@broker.internal/orders")
let ch = try mq.channel()
try ch.declareQueue("jobs", durable = true)
try ch.publish("", "jobs", "resize 7.png")
let jobs = try ch.consume("jobs")
let d = try jobs.next(5s) // ?Delivery
if !(d is none) {
print(d.text())
try d.ack()
}
try mq.close()
adm.network.s3
An S3 client. It signs requests with AWS Signature Version 4 itself, in ADM, over std.net.http: there is no SDK to install. It talks to Amazon S3 and to the servers that speak its API (MinIO, Ceph, Garage, Cloudflare R2, Backblaze B2).
adm get admlang:adm.network.s3
use adm.network.s3
let store = try s3.connect("https://s3.eu-central-1.amazonaws.com", accessKey, secretKey)
let photos = store.bucket("photos")
try photos.put("2026/cat.jpg", pixels, "image/jpeg")
let cat = try photos.get("2026/cat.jpg") // s3.Object, an io.Reader
let data = try cat.bytes()
for let entry in try photos.list("2026/").all() {
print("{entry.key} {entry.size}")
}
let link = try photos.presign("2026/cat.jpg", 1h) // a URL anyone can read for an hour
adm.network.memcached planned
A Memcached client. It speaks the text and meta protocols itself, in ADM, over std.net: there is no C library to install. It talks to memcached 1.6 and later and to the services that speak its protocol (ElastiCache, Memorystore).
Not built yet: the example shows the API as designed.
adm get admlang:adm.network.memcached
use adm.network.memcached
let cache = try memcached.connect("cache1.internal:11211", "cache2.internal:11211")
try cache.set("session:7", data, expire = 30min)
let session = try cache.get("session:7") // ?bytes
let hits = try cache.increment("hits", 1, initial = 0)
let many = try cache.getMany(["a", "b", "c"]) // map<string, bytes>
try cache.close()
adm.network.etcd planned
An etcd v3 client. It speaks the gRPC API over std.net.grpc, in ADM: key-value with transactions, watches, leases, locks and elections, against etcd 3.4 and later.
Not built yet: the example shows the API as designed.
adm get admlang:adm.network.etcd
use adm.network.etcd
let cluster = try etcd.connect("https://etcd1.internal:2379", "https://etcd2.internal:2379")
try cluster.put("/config/shop/theme", "dark")
let theme = try cluster.get("/config/shop/theme") // ?string
for let kv in try cluster.getPrefix("/config/shop/") {
print("{kv.key} = {kv.value}")
}
let watch = try cluster.watch("/config/shop/", prefix = true)
for let event in watch {
print("{event.kind} {event.key}")
}
let lease = try cluster.grant(10s) // kept alive until closed
try cluster.put("/workers/{id}", "alive", lease = lease)
let lock = try cluster.lock("/locks/rebuild", lease)
try lock.unlock()
AI providers
Official ADM libraries that call hosted model APIs. std.ai runs models on the machine; these libraries talk to a provider over std.net.http, in ADM, with no SDK to install. Each is published as adm.ai.<provider> and signed by the admlang publisher. They live outside the standard library because provider APIs change every few weeks, and a new model or endpoint should ship without a std release.
Source: github.com/admlang/ai
adm.ai.openai
An OpenAI API client in ADM over std.net.http. It also talks to the servers that speak the same API (OpenRouter, Groq, Mistral, DeepSeek, Together, vLLM, LM Studio, Ollama) through baseURL. Its conversations are std.ai.llm's, so tools, @tool methods, MCP tools, pictures and documents work as they do with a model loaded by std.ai.llm itself.
adm get admlang:adm.ai.openai
use adm.ai.openai
let ai = new openai.Client(model = "gpt-5-mini") // OPENAI_API_KEY; baseURL for another server
let chat = ai.chat()
chat.system = "Answer in one sentence."
chat.useTool(new Weather())
print(try chat.say("Do I need a coat in Oslo today?"))
chat.model = "gpt-5" // the conversation goes on with another model
try chat.ask("Why?")
for {
let piece = try chat.next()
break when piece is none
print(piece)
}
let vectors = try ai.embedder("text-embedding-3-small").embed(string["one", "two"])
let pictures = try ai.images.generate("a kettle on a windowsill", size = openai.ImageSize.Square)
let words = try ai.audio.transcribe(recording)
let file = try ai.files.upload("requests.jsonl", openai.Purpose.Batch)
let batch = try ai.batches.create(file.id)
adm.ai.anthropic
An Anthropic API client in ADM over std.net.http: the Messages API of the Claude models. Its conversations are std.ai.llm's, so tools, @tool methods, MCP tools, pictures and documents work as they do with a model loaded by std.ai.llm itself.
adm get admlang:adm.ai.anthropic
use adm.ai.anthropic
let ai = new anthropic.Client(model = "claude-opus-5-5") // ANTHROPIC_API_KEY
let chat = ai.chat()
chat.system = "Answer in one sentence."
chat.reasoning = llm.Reasoning.High
chat.useTool(new Weather())
print(try chat.say("Do I need a coat in Oslo today?"))
print(chat.messages()[1].thinking)
chat.model = "claude-haiku-5-5" // the conversation goes on with another model
try chat.ask("Why?")
for {
let piece = try chat.next()
break when piece is none
print(piece)
}
print(chat.usage().cachedTokens)
print(try ai.countTokens(chat))
let file = try ai.files.upload("report.pdf", "application/pdf")
let batch = try ai.batches.create([anthropic.BatchRequest{id: "q1", request: request}])
adm.ai.gemini
A Gemini API client in ADM over std.net.http, for Google's models through an API key. The same models under Google Cloud credentials are adm.cloud.gcp.vertex. Its conversations are std.ai.llm's, so tools, @tool methods, MCP tools, pictures, sound, documents and uploaded files work as they do with a model loaded by std.ai.llm itself.
adm get admlang:adm.ai.gemini
use adm.ai.gemini
let ai = new gemini.Client(model = "gemini-flash-latest") // GEMINI_API_KEY
ai.googleSearch = true
let chat = ai.chat()
let answer = try chat.say("What is the weather in Oslo today?")
for let source in gemini.groundingOf(chat.messages()[2]).sources {
print("{source.title} {source.uri}")
}
let clip = try ai.files.wait((try ai.files.upload("talk.mp4", "video/mp4")).name)
try chat.ask(llm.Message{text: "Summarise the talk.", files: [gemini.remote(clip)]})
let vectors = try ai.embedder(task = gemini.TaskType.RetrievalDocument).embed(string["one", "two"])
adm.ai.ollama
An Ollama client in ADM over std.net.http: the native API of a local or remote Ollama server. Its conversations are std.ai.llm's, so tools, @tool methods, MCP tools, pictures and documents work as they do with a model loaded by std.ai.llm itself.
adm get admlang:adm.ai.ollama
use adm.ai.ollama
let ai = new ollama.Client(model = "qwen3:4b") // the server of OLLAMA_HOST, else 127.0.0.1:11434
try ai.pull("qwen3:4b")
let chat = ai.chat()
chat.system = "Answer in one sentence."
chat.useTool(new Weather())
print(try chat.say("Do I need a coat in Oslo today?"))
chat.model = "qwen3:30b" // the conversation goes on with another model
try chat.ask("Why?")
for {
let piece = try chat.next()
break when piece is none
print(piece)
}
print(chat.usage().inputTokens)
let vectors = try ai.embedder("nomic-embed-text").embed(string["a cat", "a dog"])
for let m in try ai.models() {
print("{m.name} {m.size}")
}
Interop
Official ADM libraries that run other languages inside an ADM program. Each library embeds one engine, is published as adm.interop.<language> and is signed by the admlang publisher.
Source: github.com/admlang/interop
adm.interop.js
JavaScript inside an ADM program. The engine is QuickJS-ng; its C sources ship with the library and are compiled into your program, so there is nothing else to install.
adm get admlang:adm.interop.js
module billing {
use adm.interop.js::*
struct Item {
price float
quantity int
}
@js("billing.js")
def total(items Item[]) !float;
}
adm.interop.python
Python inside an ADM program. The engine is MicroPython 1.29; its C sources ship with the library and are compiled into your program, so there is nothing else to install.
adm get admlang:adm.interop.python
module shop {
use adm.interop.python::*
struct Customer {
name string
years int
orders int
}
@python("rules.py")
def discount(customer Customer) !float;
}
adm.interop.lua
Lua inside an ADM program. The engine is Lua 5.4; its C sources ship with the library and are compiled into your program, so there is nothing else to install.
adm get admlang:adm.interop.lua
module shop {
use adm.interop.lua::*
struct Customer {
name string
years int
orders int
}
@lua("rules.lua")
def discount(customer Customer) !float;
}
adm.interop.wasm
WebAssembly inside an ADM program, written in ADM: no C, nothing else to install.
adm get admlang:adm.interop.wasm
use adm.interop.wasm
let loaded = try wasm.decode(bytes)
let store = new wasm.Store()
let instance = try store.instantiate(loaded)
let results = try instance.invoke("add", wasm.Value[
wasm.Value{kind: wasm.ValueType.I32, bits: 2},
wasm.Value{kind: wasm.ValueType.I32, bits: 3}
])
println(results[0].bits)
adm.interop.java
Java inside an ADM program: classes, objects and static methods of the installed Java VM, called from ADM, and ADM functions Java calls back. The library loads the Java VM of the installed JDK or JRE into the program when it is first used; nothing of Java is needed to build.
adm get admlang:adm.interop.java
use adm.interop.java::*
@java("com.acme.Billing")
def total(prices float[], quantity int32) !float;
@java("com.acme.Billing")
def invoiceNumber(customer string) !?string;
adm.interop.dotnet
.NET inside an ADM program: types, objects and static methods of .NET assemblies (C#, F#, VB), called from ADM, and ADM functions .NET calls back. The library loads the installed .NET runtime into the program when it is first used; nothing of .NET is needed to build.
adm get admlang:adm.interop.dotnet
use adm.interop.dotnet::*
@dotnet("Acme.Billing")
def total(prices float[], quantity int32) !float;
@dotnet("Acme.Billing")
def invoiceNumber(customer string) !?string;
Cloud
Official ADM libraries that connect an ADM program to a cloud provider. Each library is one cloud, written in ADM over std.net.http, published as adm.cloud.<cloud> and signed by the admlang publisher. They live outside the standard library because the clouds add and change operations every week.
Source: github.com/admlang/cloud
adm.cloud.aws planned
An Amazon Web Services client in ADM over std.net.http: one module per service, and a root module that holds credentials, Signature Version 4, regions, endpoints, retries and errors. There is no SDK to install.
Not built yet: the example shows the API as designed.
adm get admlang:adm.cloud.aws
use (
adm.cloud.aws
adm.cloud.aws.sqs
adm.cloud.aws.secretsmanager
)
let account = try aws.connect("eu-central-1") // the credential chain
let queue = new sqs.Client(account).queue("https://sqs.eu-central-1.amazonaws.com/123456789012/jobs")
try queue.send("resize 7.png", delay = 10s)
for let m in try queue.receive(10, wait = 20s) {
print(m.body)
try m.delete()
}
let secrets = new secretsmanager.Client(account)
let dbPassword = try secrets.get("prod/db/password") // Secret<string>
adm.cloud.gcp planned
A Google Cloud client in ADM over std.net.http: one module per service, and a root module that holds credentials, OAuth2 tokens, projects and locations, retries and errors. There is no SDK to install.
Not built yet: the example shows the API as designed.
adm get admlang:adm.cloud.gcp
use (
adm.cloud.gcp
adm.cloud.gcp.storage
adm.cloud.gcp.pubsub
)
let project = try gcp.connect("my-project") // application default credentials
let photos = new storage.Client(project).bucket("photos")
try photos.put("2026/cat.jpg", pixels, "image/jpeg")
let topic = new pubsub.Client(project).topic("orders")
try topic.publish("order 7 paid", orderingKey = "7")
adm.cloud.azure planned
A Microsoft Azure client in ADM over std.net.http: one module per service, and a root module that holds credentials, shared keys and SAS, clouds and endpoints, retries and errors. There is no SDK to install.
Not built yet: the example shows the API as designed.
adm get admlang:adm.cloud.azure
use (
adm.cloud.azure
adm.cloud.azure.blob
adm.cloud.azure.keyvault
)
let account = try azure.connect() // the credential chain
let photos = new blob.Client(account, "mystorage").container("photos")
try photos.upload("2026/cat.jpg", pixels, "image/jpeg")
let link = try photos.sasURL("2026/cat.jpg", 1h)
let vault = new keyvault.Client(account, "https://myvault.vault.azure.net")
let dbPassword = try vault.secret("db-password") // Secret<string>
adm.cloud.cloudflare planned
A Cloudflare API client in ADM over std.net.http: one module per product, and a root module that holds API tokens, accounts and zones, the v4 API envelope, pagination, retries and errors.
Not built yet: the example shows the API as designed.
adm get admlang:adm.cloud.cloudflare
use (
adm.cloud.cloudflare
adm.cloud.cloudflare.kv
adm.cloud.cloudflare.dns
)
let cf = try cloudflare.connect(token) // CLOUDFLARE_API_TOKEN when left out
let settings = new kv.Client(cf.account("0123abcd")).namespace("settings")
try settings.put("theme", "dark", expire = 7d)
let theme = try settings.get("theme") // ?string
let zone = try cf.zone("example.com")
try new dns.Client(zone).create("A", "www", "203.0.113.7", proxied = true)
Developer tools
Official ADM libraries for the tools around a codebase: version control, containers, clusters and the hosting platforms. Each is written in ADM over std.net, published as adm.dev.<name> and signed by the admlang publisher. They live outside the standard library because these APIs change with every release of the product behind them.
Source: github.com/admlang/dev
adm.dev.git planned
Git in ADM: repositories on disk, their objects and references, and the smart protocol over HTTP and SSH. There is no libgit2 and no git binary to call.
Not built yet: the example shows the API as designed.
adm get admlang:adm.dev.git
use adm.dev.git
let repo = try git.clone("https://github.com/admlang/examples.git", "examples", depth = 1)
for let commit in try repo.log("main", limit = 10) {
print("{commit.id.short()} {commit.summary}")
}
let tree = try repo.tree("main")
let readme = try tree.file("README.md").text()
try repo.add("notes.md")
let id = try repo.commit("Add notes", author = new git.Signature("Ana", "ana@example.com"))
try repo.push("origin", "main", credentials = token)
adm.dev.github planned
A GitHub API client in ADM over std.net.http: REST and GraphQL, webhooks, and the three ways to be someone on GitHub (a token, an OAuth app, a GitHub App). GitHub Enterprise Server works through a base URL.
Not built yet: the example shows the API as designed.
adm get admlang:adm.dev.github
use adm.dev.github
let gh = try github.connect(token) // GITHUB_TOKEN when left out
let repo = gh.repo("admlang", "examples")
for let issue in try repo.issues(state = github.State.Open).all() {
print("#{issue.number} {issue.title}")
}
let pr = try repo.pullRequests.create("feature", "main", title = "Add geo examples")
try pr.comment("Ready for review")
// a webhook endpoint
let hooks = new github.Webhooks(secret)
server.handle("/github", hooks.handler((event github.Event) => print(event.name)))
adm.dev.gitlab planned
A GitLab API client in ADM over std.net.http: REST and GraphQL, webhooks, and the tokens GitLab issues to people, projects, groups and CI jobs. Self-managed instances work through a base URL.
Not built yet: the example shows the API as designed.
adm get admlang:adm.dev.gitlab
use adm.dev.gitlab
let gl = try gitlab.connect(token, url = "https://gitlab.example.com") // GITLAB_TOKEN, gitlab.com when left out
let project = gl.project("group/app")
for let mr in try project.mergeRequests(state = gitlab.State.Opened).all() {
print("!{mr.iid} {mr.title}")
}
let pipeline = try project.pipelines.create("main", variables = {"DEPLOY": "staging"})
adm.dev.docker planned
A Docker Engine API client in ADM over std.net.http, on a Unix socket or TCP with TLS. It also talks to Podman through its Docker-compatible API.
Not built yet: the example shows the API as designed.
adm get admlang:adm.dev.docker
use adm.dev.docker
let engine = try docker.connect() // DOCKER_HOST, else the local socket
try engine.images.pull("postgres:17")
let pg = try engine.containers.create("postgres:17", name = "db", env = {"POSTGRES_PASSWORD": "secret"}, ports = {"5432/tcp": 5432})
try pg.start()
for let line in try pg.logs(follow = true) {
print(line.text)
}
let result = try pg.exec("pg_isready")
try pg.stop()
try pg.remove(volumes = true)
adm.dev.kubernetes planned
A Kubernetes API client in ADM over std.net.http: the API server, its resources typed or unstructured, and the credentials a kubeconfig or a pod carries.
Not built yet: the example shows the API as designed.
adm get admlang:adm.dev.kubernetes
use adm.dev.kubernetes
let cluster = try kubernetes.connect() // KUBECONFIG or ~/.kube/config, else in-cluster
let pods = cluster.pods("shop")
for let pod in try pods.list(labels = "app=web") {
print("{pod.name} {pod.status.phase}")
}
for let event in try pods.watch(labels = "app=web") {
print("{event.kind} {event.object.name}")
}
let logs = try pods.logs("web-7f9d", container = "web", tail = 100)
try cluster.deployments("shop").scale("web", 3)
Sign-in
Official ADM libraries for signing users in with the identity providers most applications offer. The protocols (OAuth 2.0, OpenID Connect, JOSE) are in std.proto.auth; each library here knows one provider: its endpoints, scopes, credential format, the quirks of its tokens, and where its profile data comes from. They are written in ADM, published as adm.auth.<provider> and signed by the admlang publisher, and live outside the standard library because providers change these details without notice.
Source: github.com/admlang/auth
adm.auth.google planned
Sign in with Google in ADM over std.proto.auth.openid: the web flow, ID tokens from One Tap and mobile apps, refresh, and Google Workspace domain checks.
Not built yet: the example shows the API as designed.
adm get admlang:adm.auth.google
use adm.auth.google
let provider = new google.Provider(clientId, clientSecret, redirectURL = "https://example.com/auth/google")
let start = try provider.begin(scopes = ["email", "profile"])
// send the browser to start.url, keep start.state in the session
let login = try provider.complete(callbackURL, start)
let profile = login.profile
print("{profile.name} <{profile.email}>")
// a token Google Identity Services or a mobile app obtained
let claims = try provider.verifyIdToken(idToken)
adm.auth.github planned
Sign in with GitHub in ADM over std.proto.auth.oauth2. GitHub has no OpenID Connect, so the library completes the OAuth flow and reads the profile from the user and emails API. It also offers the device flow for command-line tools, GitHub App user tokens, and GitHub Enterprise Server through a base URL.
Not built yet: the example shows the API as designed.
adm get admlang:adm.auth.github
use adm.auth.github
let provider = new github.Provider(clientId, clientSecret, redirectURL = "https://example.com/auth/github")
let start = try provider.begin(scopes = ["read:user", "user:email"])
let login = try provider.complete(callbackURL, start)
print(login.profile.email) // the primary verified address
// a command-line tool
let device = try provider.beginDevice(scopes = ["repo"])
print("Open {device.url} and enter {device.code}")
let token = try device.wait()
adm.auth.microsoft planned
Sign in with Microsoft in ADM over std.proto.auth.openid: Entra ID (work and school accounts), personal Microsoft accounts, tenants and their issuers, certificates as client credentials, and the profile from Microsoft Graph.
Not built yet: the example shows the API as designed.
adm get admlang:adm.auth.microsoft
use adm.auth.microsoft
let provider = new microsoft.Provider(clientId, clientSecret, tenant = "organizations",
redirectURL = "https://example.com/auth/microsoft")
let start = try provider.begin(scopes = ["openid", "profile", "email", "User.Read"])
let login = try provider.complete(callbackURL, start)
print(login.profile.email)
print(login.claims.get("tid")) // the user's tenant
adm.auth.apple planned
Sign in with Apple in ADM over std.proto.auth.openid: the web and native flows, the client secret Apple wants as a signed JWT, the user's name that arrives only once, private relay addresses, and Apple's server-to-server notifications.
Not built yet: the example shows the API as designed.
adm get admlang:adm.auth.apple
use adm.auth.apple
let provider = new apple.Provider(servicesId, teamId, keyId, privateKey,
redirectURL = "https://example.com/auth/apple")
let start = try provider.begin(scopes = ["name", "email"])
let login = try provider.complete(callbackForm, start) // Apple posts the result as a form
let profile = login.profile
print(profile.name) // present on the first sign-in only: store it
print(profile.email) // may be a privaterelay.appleid.com address
// a token a native app obtained
let claims = try provider.verifyIdToken(idToken, audience = bundleId)
Notifications
Official ADM libraries that send messages to people through the services most teams use: chat platforms, SMS and voice, transactional mail. Each is written in ADM over std.net.http, published as adm.notify.<service> and signed by the admlang publisher. They live outside the standard library because these APIs change with the product; mail over SMTP and IMAP is in std.net.mail, which does not.
Source: github.com/admlang/notify
adm.notify.slack planned
A Slack client in ADM over std.net.http: the Web API, Block Kit, incoming webhooks, and the two ways Slack calls an app back (HTTP with signed requests, Socket Mode over WebSocket).
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.slack
use adm.notify.slack
let bot = new slack.Client(botToken) // SLACK_BOT_TOKEN when left out
try bot.send("#deploys", "Release 1.4 is out")
let message = new slack.Message()
message.section("*Release 1.4* is out")
message.button("Release notes", url = "https://example.com/notes/1.4")
try bot.send("#deploys", message)
try bot.upload("#deploys", report, "report.pdf")
// a hook that posts without a bot
try slack.webhook(hookURL).send("Build passed")
adm.notify.discord planned
A Discord client in ADM over std.net.http: the REST API, webhooks, application commands with interactions, and the Gateway for bots that stay connected.
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.discord
use adm.notify.discord
let bot = new discord.Client(botToken) // DISCORD_TOKEN when left out
let embed = new discord.Embed("Release 1.4", "Now available")
embed.field("Downloads", "https://example.com/1.4")
try bot.send(channelId, "Release 1.4 is out", embeds = [embed])
try discord.webhook(hookURL).send("Build passed")
adm.notify.telegram planned
A Telegram Bot API client in ADM over std.net.http: messages, media, keyboards, and updates by long polling or webhook.
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.telegram
use adm.notify.telegram
let bot = new telegram.Bot(token) // TELEGRAM_BOT_TOKEN when left out
try bot.send(chatId, "Release 1.4 is out", parseMode = telegram.ParseMode.MarkdownV2)
let keyboard = new telegram.InlineKeyboard()
keyboard.button("Release notes", url = "https://example.com/notes/1.4")
try bot.send(chatId, "Details:", keyboard = keyboard)
for let update in try bot.updates() { // long polling
let message = update.message
continue when message is none
try bot.send(message.chat.id, "Got it")
}
adm.notify.twilio planned
A Twilio client in ADM over std.net.http: SMS and MMS, WhatsApp, voice with TwiML, Verify, and the webhooks Twilio sends with their signatures checked.
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.twilio
use adm.notify.twilio
let tw = new twilio.Client(accountSid, authToken) // TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN
let sms = try tw.messages.send("+15005550006", "+15551234567", "Your code is 482913")
print(sms.sid)
let check = try tw.verify("VA...").start("+15551234567", channel = twilio.Channel.Sms)
let ok = try tw.verify("VA...").check("+15551234567", code)
adm.notify.sendgrid planned
A SendGrid client in ADM over std.net.http: mail send v3 from std.formats.mail messages or dynamic templates, suppressions, the event webhook, inbound parse.
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.sendgrid
use (
adm.notify.sendgrid
std.formats.mail
)
let sg = new sendgrid.Client(apiKey) // SENDGRID_API_KEY when left out
let message = new mail.Message()
message.from = "Shop <orders@example.com>"
message.to = ["ana@example.net"]
message.subject = "Your order"
message.text = "Thanks, Ana."
try sg.send(message)
try sg.send(new sendgrid.Template("d-1234", to = "ana@example.net", data = {"name": "Ana", "order": "7"}))
adm.notify.postmark planned
A Postmark client in ADM over std.net.http: transactional mail from std.formats.mail messages or templates, batches, message streams, bounces, and Postmark's webhooks.
Not built yet: the example shows the API as designed.
adm get admlang:adm.notify.postmark
use (
adm.notify.postmark
std.formats.mail
)
let pm = new postmark.Client(serverToken) // POSTMARK_SERVER_TOKEN when left out
let message = new mail.Message()
message.from = "Shop <orders@example.com>"
message.to = ["ana@example.net"]
message.subject = "Your order"
message.html = "<p>Thanks, Ana.</p>"
let sent = try pm.send(message, stream = "outbound", tag = "order")
print(sent.messageId)
Payments
Official ADM libraries that take payments through a provider. Each is written in ADM over std.net.http, published as adm.payment.<provider> and signed by the admlang publisher. They live outside the standard library because payment APIs are versioned by date and change often, and a fix must ship the week the provider changes something.
Source: github.com/admlang/payment
adm.payment.stripe planned
A Stripe client in ADM over std.net.http: the core objects of a business that charges cards and runs subscriptions, and the webhooks that tell it what happened.
Not built yet: the example shows the API as designed.
adm get admlang:adm.payment.stripe
use adm.payment.stripe
let st = new stripe.Client(secretKey) // STRIPE_SECRET_KEY when left out
let customer = try st.customers.create(email = "ana@example.net", name = "Ana")
let intent = try st.paymentIntents.create(2499, "eur", customer = customer.id, idempotencyKey = orderId)
print(intent.clientSecret) // handed to Stripe.js in the browser
let session = try st.checkout.sessions.create(mode = stripe.CheckoutMode.Subscription,
price = "price_123", successURL = "https://example.com/done", cancelURL = "https://example.com/cart")
// the webhook endpoint
let hooks = new stripe.Webhooks(endpointSecret)
server.handle("/stripe", hooks.handler((event stripe.Event) => orders.record(event)))
adm.payment.paypal planned
A PayPal client in ADM over std.net.http: the REST APIs (orders, payments, subscriptions, payouts), their webhooks, and verification of IPN messages for integrations that still receive them.
Not built yet: the example shows the API as designed.
adm get admlang:adm.payment.paypal
use adm.payment.paypal
let pp = try paypal.connect(clientId, secret, sandbox = true) // PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET
let order = try pp.orders.create(new paypal.Money(2499, "EUR"), returnURL = "https://example.com/done", cancelURL = "https://example.com/cart")
print(order.approveLink) // where the buyer goes
let captured = try pp.orders.capture(order.id)
let hooks = new paypal.Webhooks(pp, webhookId)
server.handle("/paypal", hooks.handler((event paypal.Event) => orders.record(event)))
Telemetry
Official ADM libraries that send what the Telemetry service of std.services.telemetry collects (metrics, logs, errors, usage events, spans) to a vendor. The service itself ships OTLP, file and console exporters; a vendor's own ingestion API changes with the vendor, so each is a library, written in ADM over std.net.http, published as adm.telemetry.<vendor> and signed by the admlang publisher.
Source: github.com/admlang/telemetry
adm.telemetry.datadog planned
A Datadog exporter for the Telemetry service, in ADM over std.net.http: metrics, logs, events and traces to the intake APIs of a Datadog site, with no agent in between.
Not built yet: the example shows the API as designed.
adm get admlang:adm.telemetry.datadog
use (
std.services.telemetry::(Telemetry, TelemetryConfig)
adm.telemetry.datadog
)
let dd = new datadog.Exporter(apiKey, site = "datadoghq.eu") // DD_API_KEY, DD_SITE when left out
dd.service = "shop"
dd.env = "prod"
dd.version = "1.4.0"
dd.tags = ["team:payments"]
let config = new TelemetryConfig()
config.exporters = [dd]
try await Telemetry.start(config)
adm.telemetry.sentry planned
A Sentry exporter for the Telemetry service, in ADM over std.net.http: errors as Sentry events with stack traces and breadcrumbs, spans as transactions, and check-ins for scheduled jobs. It speaks the envelope protocol, so it works with sentry.io, self-hosted Sentry and GlitchTip.
Not built yet: the example shows the API as designed.
adm get admlang:adm.telemetry.sentry
use (
std.services.telemetry::(Telemetry, TelemetryConfig)
adm.telemetry.sentry
)
let st = new sentry.Exporter(dsn) // SENTRY_DSN when left out
st.release = "shop@1.4.0"
st.environment = "prod"
st.sampleRate = 0.2 // of transactions; errors always go
let config = new TelemetryConfig()
config.exporters = [st]
try await Telemetry.start(config)