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.

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

terminal
adm get admlang:adm.database.sqlite
sqlite.adm
use (
	std.data.db
	adm.database.sqlite
)

let conn = try db.connect("sqlite:app.db")

README and source

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.

terminal
adm get admlang:adm.database.postgres
postgres.adm
use (
	std.data.db
	adm.database.postgres
)

let conn = try db.connect("postgres://app:secret@db.internal:5432/shop?sslmode=verify-full")

README and source

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.

terminal
adm get admlang:adm.database.mysql
mysql.adm
use (
	std.data.db
	adm.database.mysql
)

let conn = try db.connect("mysql://app:secret@db.internal:3306/shop?sslmode=verify-full")

README and source

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.

terminal
adm get admlang:adm.database.mongodb
mongodb.adm
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()

README and source

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

terminal
adm get admlang:adm.database.store
store.adm
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()

README and source

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.

terminal
adm get admlang:adm.database.orm
orm.adm
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()

README and source

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.

terminal
adm get admlang:adm.database.elasticsearch
elasticsearch.adm
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}")
}

README and source

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.

terminal
adm get admlang:adm.network.redis
redis.adm
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()

README and source

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

terminal
adm get admlang:adm.network.kafka
kafka.adm
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()

README and source

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

terminal
adm get admlang:adm.network.amqp
amqp.adm
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()

README and source

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

terminal
adm get admlang:adm.network.s3
s3.adm
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

README and source

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.

terminal
adm get admlang:adm.network.memcached
memcached.adm
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()

README and source

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.

terminal
adm get admlang:adm.network.etcd
etcd.adm
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()

README and source

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.

terminal
adm get admlang:adm.ai.openai
openai.adm
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)

README and source

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.

terminal
adm get admlang:adm.ai.anthropic
anthropic.adm
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}])

README and source

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.

terminal
adm get admlang:adm.ai.gemini
gemini.adm
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"])

README and source

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.

terminal
adm get admlang:adm.ai.ollama
ollama.adm
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}")
}

README and source

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.

terminal
adm get admlang:adm.interop.js
js.adm
module billing {
	use adm.interop.js::*

	struct Item {
		price    float
		quantity int
	}

	@js("billing.js")
	def total(items Item[]) !float;
}

README and source

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.

terminal
adm get admlang:adm.interop.python
python.adm
module shop {
	use adm.interop.python::*

	struct Customer {
		name   string
		years  int
		orders int
	}

	@python("rules.py")
	def discount(customer Customer) !float;
}

README and source

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.

terminal
adm get admlang:adm.interop.lua
lua.adm
module shop {
	use adm.interop.lua::*

	struct Customer {
		name   string
		years  int
		orders int
	}

	@lua("rules.lua")
	def discount(customer Customer) !float;
}

README and source

adm.interop.wasm

WebAssembly inside an ADM program, written in ADM: no C, nothing else to install.

terminal
adm get admlang:adm.interop.wasm
wasm.adm
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)

README and source

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.

terminal
adm get admlang:adm.interop.java
java.adm
use adm.interop.java::*

@java("com.acme.Billing")
def total(prices float[], quantity int32) !float;

@java("com.acme.Billing")
def invoiceNumber(customer string) !?string;

README and source

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.

terminal
adm get admlang:adm.interop.dotnet
dotnet.adm
use adm.interop.dotnet::*

@dotnet("Acme.Billing")
def total(prices float[], quantity int32) !float;

@dotnet("Acme.Billing")
def invoiceNumber(customer string) !?string;

README and source

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.

terminal
adm get admlang:adm.cloud.aws
aws.adm
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>

README and source

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.

terminal
adm get admlang:adm.cloud.gcp
gcp.adm
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")

README and source

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.

terminal
adm get admlang:adm.cloud.azure
azure.adm
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>

README and source

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.

terminal
adm get admlang:adm.cloud.cloudflare
cloudflare.adm
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)

README and source

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.

terminal
adm get admlang:adm.dev.git
git.adm
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)

README and source

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.

terminal
adm get admlang:adm.dev.github
github.adm
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)))

README and source

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.

terminal
adm get admlang:adm.dev.gitlab
gitlab.adm
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"})

README and source

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.

terminal
adm get admlang:adm.dev.docker
docker.adm
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)

README and source

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.

terminal
adm get admlang:adm.dev.kubernetes
kubernetes.adm
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)

README and source

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.

terminal
adm get admlang:adm.auth.google
google.adm
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)

README and source

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.

terminal
adm get admlang:adm.auth.github
github.adm
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()

README and source

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.

terminal
adm get admlang:adm.auth.microsoft
microsoft.adm
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

README and source

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.

terminal
adm get admlang:adm.auth.apple
apple.adm
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)

README and source

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.

terminal
adm get admlang:adm.notify.slack
slack.adm
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")

README and source

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.

terminal
adm get admlang:adm.notify.discord
discord.adm
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")

README and source

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.

terminal
adm get admlang:adm.notify.telegram
telegram.adm
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")
}

README and source

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.

terminal
adm get admlang:adm.notify.twilio
twilio.adm
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)

README and source

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.

terminal
adm get admlang:adm.notify.sendgrid
sendgrid.adm
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"}))

README and source

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.

terminal
adm get admlang:adm.notify.postmark
postmark.adm
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)

README and source

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.

terminal
adm get admlang:adm.payment.stripe
stripe.adm
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)))

README and source

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.

terminal
adm get admlang:adm.payment.paypal
paypal.adm
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)))

README and source

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.

terminal
adm get admlang:adm.telemetry.datadog
datadog.adm
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)

README and source

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.

terminal
adm get admlang:adm.telemetry.sentry
sentry.adm
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)

README and source