Learn NTNT

From install to verified app in four prompts.

⚠️ Early stage: NTNT is in active development. The language is stable and running production apps, but expect breaking changes between minor versions.

Step 1: Your First App

Pick your tool and copy the prompt. Your agent handles the install, the syntax, and the demo.

Install NTNT (curl -sSf https://raw.githubusercontent.com/ntntlang/ntnt/main/install.sh | bash), then run `ntnt learn claude-code` and read the generated rules before writing any code.

Build a web app in server.tnt: a GET / that returns a greeting and a GET /api/status that returns JSON. Add a requires contract on one handler. Make the app look good. Write a server.intent file with scenarios for both routes. Run `ntnt intent check server.tnt` until all pass, then `ntnt run server.tnt`. Respond with the URL of the running app.
Install NTNT (curl -sSf https://raw.githubusercontent.com/ntntlang/ntnt/main/install.sh | bash), then run `ntnt learn codex` and read the generated rules before writing any code.

Build a web app in server.tnt: a GET / that returns a greeting and a GET /api/status that returns JSON. Add a requires contract on one handler. Make the app look good. Write a server.intent file with scenarios for both routes. Run `ntnt intent check server.tnt` until all pass, then `ntnt run server.tnt`. Respond with the URL of the running app.
Install NTNT (curl -sSf https://raw.githubusercontent.com/ntntlang/ntnt/main/install.sh | bash), then run `ntnt learn cursor` and read the generated rules before writing any code.

Build a web app in server.tnt: a GET / that returns a greeting and a GET /api/status that returns JSON. Add a requires contract on one handler. Make the app look good. Write a server.intent file with scenarios for both routes. Run `ntnt intent check server.tnt` until all pass, then `ntnt run server.tnt`. Respond with the URL of the running app.
Install NTNT (curl -sSf https://raw.githubusercontent.com/ntntlang/ntnt/main/install.sh | bash), then run `ntnt learn` and read the generated rules before writing any code.

Build a web app in server.tnt: a GET / that returns a greeting and a GET /api/status that returns JSON. Add a requires contract on one handler. Make the app look good. Write a server.intent file with scenarios for both routes. Run `ntnt intent check server.tnt` until all pass, then `ntnt run server.tnt`. Respond with the URL of the running app.

ntnt learn generates platform-native config files so your agent loads the rules every session. One command, permanent knowledge.

Step 2: Add a Database

Once your demo is running, add persistence.

Add a SQLite database to the app. Create a messages table, add POST /messages to save a message and GET /messages to list them. Update server.intent with scenarios for both endpoints. Run `ntnt intent check server.tnt` until all pass. Add a link to the homepage to /messages and a form that posts a new message.
Add a SQLite database to the app. Create a messages table, add POST /messages to save a message and GET /messages to list them. Update server.intent with scenarios for both endpoints. Run `ntnt intent check server.tnt` until all pass. Add a link to the homepage to /messages and a form that posts a new message.
Add a SQLite database to the app. Create a messages table, add POST /messages to save a message and GET /messages to list them. Update server.intent with scenarios for both endpoints. Run `ntnt intent check server.tnt` until all pass. Add a link to the homepage to /messages and a form that posts a new message.
Add a SQLite database to the app. Create a messages table, add POST /messages to save a message and GET /messages to list them. Update server.intent with scenarios for both endpoints. Run `ntnt intent check server.tnt` until all pass. Add a link to the homepage to /messages and a form that posts a new message.

Step 3: Add Authentication

Lock it down so only logged-in users can access the site.

Add session-based authentication using cookies and SQLite. Create a login page, protect the homepage and /messages so only logged-in users can access the site, and redirect unauthenticated users to /login. Add a logout link to the app. Add contracts on the protected handlers. Update server.intent to test login page renders, unauthenticated access redirects and POSTS and run `ntnt intent check server.tnt` until all pass.
Add session-based authentication using cookies and SQLite. Create a login page, protect the homepage and /messages so only logged-in users can access the site, and redirect unauthenticated users to /login. Add a logout link to the app. Add contracts on the protected handlers. Update server.intent to test login page renders, unauthenticated access redirects and POSTS and run `ntnt intent check server.tnt` until all pass.
Add session-based authentication using cookies and SQLite. Create a login page, protect the homepage and /messages so only logged-in users can access the site, and redirect unauthenticated users to /login. Add a logout link to the app. Add contracts on the protected handlers. Update server.intent to test login page renders, unauthenticated access redirects and POSTS and run `ntnt intent check server.tnt` until all pass.
Add session-based authentication using cookies and SQLite. Create a login page, protect the homepage and /messages so only logged-in users can access the site, and redirect unauthenticated users to /login. Add a logout link to the app. Add contracts on the protected handlers. Update server.intent to test login page renders, unauthenticated access redirects and POSTS and run `ntnt intent check server.tnt` until all pass.

Step 4: Refactor

Production structure: middleware, routing, templates, and shared code.

Refactor the app to use middleware, file-based routing, external HTML templates in a views/ directory for all pages, partials for nav and footer and add a static CSS file. Move functions to lib folder. Make sure it still looks decent. Verify `ntnt intent check server.tnt` still passes.

Four prompts. You’ve got a full-stack web app with a database, auth, templates, middleware, and file-based routing. You didn’t write any NTNT. Your agent did, and ntnt intent check proved it’s correct at every step.

Advanced Demo Prompts

Step 5: PostgreSQL & Docker requires Docker

Real database, containerized.

Migrate from SQLite to PostgreSQL. Create a Dockerfile and docker-compose.yml with the ntnt app and a postgres container. Use environment variables for the connection string. Verify `ntnt intent check server.tnt` still passes. Leave the app running when you're done.

Step 6: Background Jobs

Scheduled jobs, on-demand triggers, and a job log.

Add a background job that fetches a new random cat fact from an API every 10 minutes and stores it in the database. Display the latest cat fact on the homepage with a button that schedules an immediate job to fetch a new one. Add a job log page that shows a record of all jobs run with their status and timestamps. Verify `ntnt intent check server.tnt` still passes.

Installation

macOS / Linux:

bash
curl -sSf https://raw.githubusercontent.com/ntntlang/ntnt/main/install.sh | bash

Windows (PowerShell):

powershell
irm https://raw.githubusercontent.com/ntntlang/ntnt/main/install.ps1 | iex

Hello World

bash
echo 'print("Hello, World!")' > hello.tnt
ntnt run hello.tnt

A Web API

ntnt
// api.tnt
import { json } from "std/http/server"

fn home(req) {
    return json(map { "message": "Hello!" })
}

fn get_user(req) {
    return json(map { "id": req.params["id"] })
}

get("/", home)
get("/users/{id}", get_user)

listen(3000)
bash
ntnt run api.tnt
# Visit http://localhost:3000

Hot-reload: HTTP servers automatically reload when you edit the source file.

Intent-Driven Development

Write .intent files describing features, link code with @implements, and verify everything matches.

1Write an intent file
intent
# server.intent

## Glossary

| Term | Means |
|------|-------|
| a user visits the {path} | GET {path} |
| a user visits {path} | GET {path} |
| home page | / |
| page loads successfully | status: 200 |
| they see "$text" | body contains "$text" |

---

Feature: User Greeting
  id: feature.greeting
  description: "Display a personalized greeting"

  Scenario: Greet by name
    When a user visits /?name=Alice
    → page loads successfully
    → they see "Hello, Alice"

  Scenario: Default greeting
    When a user visits the home page
    → page loads successfully
    → they see "Hello, World"
2Annotate your implementation
ntnt
// server.tnt
import { html } from "std/http/server"

// @implements: feature.greeting
fn home(req) {
    let name = req.query_params["name"] ?? "World"
    return html("<h1>Hello, #{name}!</h1>")
}

get("/", home)
listen(8080)
3Verify
bash
$ ntnt intent check server.tnt

 Feature: User Greeting
   Greet by name
   Default greeting

Features: 1 passed | Scenarios: 2 passed | Assertions: 4 passed

Design by Contract

ntnt
fn withdraw(amount: Int) -> Int
    requires amount > 0
    requires amount <= self.balance
    ensures result >= 0
{
    self.balance = self.balance - amount
    return self.balance
}

// requires fails → 400 Bad Request
// ensures fails  → 500 Internal Server Error

Standard Library

Everything’s built in. No package manager.

Webstd/http/server, std/httpHTTP server with routing, middleware, static files; HTTP client
Datastd/json, std/csv, std/db/postgres, std/db/sqliteParse and stringify; PostgreSQL and SQLite with transactions
I/Ostd/fs, std/path, std/envFile operations, path manipulation, environment variables
Textstd/string, std/urlSplit, join, trim, replace; URL encode/decode/parse
Key-Valuestd/kv, std/db/redisUnified KV store (Redis, Valkey, DragonflyDB, SQLite, in-memory); native Redis client
Authstd/authOAuth, JWT, session management
Jobsstd/jobsBackground job DSL with priority queues, cron, unique jobs, retry, dead letters. Memory, PostgreSQL, and Redis backends
Concurrencystd/concurrentSpawn, channels (send/recv), select, parallel, race, schedule, after
Utilitiesstd/time, std/math, std/cryptoTimestamps, formatting; trig, log, exp; SHA256, AES-256-GCM, Argon2, UUID
Loggingstd/logStructured request logging, custom loggers

CLI Commands

ntnt run file.tntRun a program
ntnt test server.tnt --get /healthTest HTTP endpoints (start, request, stop)
ntnt lint file.tntCheck for errors
ntnt intent check file.tntVerify code matches intent
ntnt intent studio file.intentLive visual test feedback
ntnt intent coverage file.tntFeature coverage report
ntnt validate file.tntValidate with JSON output
ntnt inspect file.tntProject structure as JSON
ntnt learn <platform>Set up AI agent config (claude-code, cursor, codex, copilot)
ntnt check file.tntQuick syntax check
ntnt worker file.tntStart background job workers for jobs defined in a file
ntnt jobs statusQueue depths, completed/failed/dead counts
ntnt jobs listList jobs by status, queue, or type
ntnt jobs inspect <id>Detailed view of a single job
ntnt jobs retry <id>Re-queue a failed or dead job
ntnt workers statusLive worker status and active jobs
ntnt workers scaleScale worker pools at runtime via control socket
ntnt migrate .Migrate old {expr} interpolation to #{expr}
ntnt docs std/stringLook up module/function docs
ntnt completionsGenerate shell completion scripts (bash, zsh, fish)
ntnt replInteractive REPL

Documentation