diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..0baf015
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1 @@
+/_site
diff --git a/docs/api/.gitignore b/docs/api/.gitignore
new file mode 100644
index 0000000..e8079a3
--- /dev/null
+++ b/docs/api/.gitignore
@@ -0,0 +1,5 @@
+###############
+# temp file #
+###############
+*.yml
+.manifest
diff --git a/docs/api/index.md b/docs/api/index.md
new file mode 100644
index 0000000..daef585
--- /dev/null
+++ b/docs/api/index.md
@@ -0,0 +1,5 @@
+# API Documentation
+
+You can browse the latest jwt.net API Documentation[^1] [here](NATS.Jwt.yml).
+
+[^1]: API Documentation is auto-generated using [DocFX](https://dotnet.github.io/docfx/).
diff --git a/docs/docfx.json b/docs/docfx.json
new file mode 100644
index 0000000..8584ef8
--- /dev/null
+++ b/docs/docfx.json
@@ -0,0 +1,65 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "files": [ "**/*.csproj" ],
+ "src": "../src"
+ }
+ ],
+ "dest": "api",
+ "includePrivateMembers": false,
+ "disableGitFeatures": false,
+ "disableDefaultFilter": false,
+ "noRestore": false,
+ "namespaceLayout": "nested",
+ "memberLayout": "separatePages",
+ "allowCompilationErrors": false
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md"
+ ]
+ },
+ {
+ "files": [
+ "documentation/**.md",
+ "documentation/**/toc.yml",
+ "toc.yml",
+ "*.md"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**"
+ ]
+ }
+ ],
+ "output": "_site",
+ "globalMetadataFiles": [],
+ "fileMetadataFiles": [],
+ "template": [
+ "default",
+ "modern",
+ "nats_template"
+ ],
+ "postProcessors": [],
+ "keepFileLink": false,
+ "disableGitFeatures": false,
+ "markdownEngineName": "markdig",
+ "markdownEngineProperties": {
+ "markdigExtensions": [
+ "abbreviations",
+ "definitionlists",
+ "tasklists",
+ "footnotes"
+ ]
+ }
+ }
+}
diff --git a/docs/documentation/intro.md b/docs/documentation/intro.md
new file mode 100644
index 0000000..f82c528
--- /dev/null
+++ b/docs/documentation/intro.md
@@ -0,0 +1,26 @@
+# Introduction
+
+This is a .NET implementation of the JWT library for the NATS ecosystem.
+
+A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys.net) to digitally sign
+JWT tokens for the [NATS](https://nats.io/) ecosystem.
+
+See also https://github.com/nats-io/jwt
+
+## Installation
+
+Reference [Jwt.net NuGet package](https://www.nuget.org/packages/jwt.net) in your project:
+You can install the package via NuGet:
+
+```bash
+dotnet add package NATS.Jwt --prerelease
+```
+
+## Usage
+
+[!code-csharp[](../../tests/NATS.Jwt.DocsExamples/IntroPage.cs#nats-jwt)]
+
+
+## What's Next
+
+*Documentation is in progress.* Help us improve the documentation by contributing today!
diff --git a/docs/documentation/toc.yml b/docs/documentation/toc.yml
new file mode 100644
index 0000000..51806d4
--- /dev/null
+++ b/docs/documentation/toc.yml
@@ -0,0 +1,5 @@
+- name: Introduction
+ href: intro.md
+
+- name: Updating Documentation
+ href: update-docs.md
diff --git a/docs/documentation/update-docs.md b/docs/documentation/update-docs.md
new file mode 100644
index 0000000..49c1d7e
--- /dev/null
+++ b/docs/documentation/update-docs.md
@@ -0,0 +1,24 @@
+# Updating Documentation
+
+As well as being able to edit pages on GitHub, you can also edit and update this documentation,
+view locally and submit a Pull Request to be included in this documentation site.
+
+## Running DocFX locally
+
+You mush have [DocFX installed](https://dotnet.github.io/docfx/):
+Clone the jwt.net ([nats-io/jwt.net](https://github.com/nats-io/jwt.net)) repository, then `cd docs` and run local server (`docfx --serve`) to view this documentation site.
+
+```
+dotnet tool update -g docfx
+```
+
+Generate API documentation and run local server:
+```
+git clone https://github.com/nats-io/jwt.net.git
+cd jwt.net/docs
+docfx --serve
+```
+
+You might not be a .NET developer, but still want to contribute to the documentation.
+You can install the .NET SDK and use the `docfx` tool to generate the documentation.
+Download the .NET SDK from [dot.net](https://dot.net).
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..0943bde
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,26 @@
+# Introduction
+
+This is a .NET implementation of the JWT library for the NATS ecosystem.
+
+A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys.net) to digitally sign
+JWT tokens for the [NATS](https://nats.io/) ecosystem.
+
+See also https://github.com/nats-io/jwt
+
+## Installation
+
+Reference [Jwt.net NuGet package](https://www.nuget.org/packages/jwt.net) in your project:
+You can install the package via NuGet:
+
+```bash
+dotnet add package NATS.Jwt --prerelease
+```
+
+## Usage
+
+[!code-csharp[](../tests/NATS.Jwt.DocsExamples/IntroPage.cs#nats-jwt)]
+
+
+## What's Next
+
+*Documentation is in progress.* Help us improve the documentation by contributing today!
diff --git a/docs/nats_template/favicon.ico b/docs/nats_template/favicon.ico
new file mode 100644
index 0000000..9464855
Binary files /dev/null and b/docs/nats_template/favicon.ico differ
diff --git a/docs/nats_template/logo.svg b/docs/nats_template/logo.svg
new file mode 100644
index 0000000..950d0a2
--- /dev/null
+++ b/docs/nats_template/logo.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/docs/toc.yml b/docs/toc.yml
new file mode 100644
index 0000000..e3639aa
--- /dev/null
+++ b/docs/toc.yml
@@ -0,0 +1,12 @@
+- name: Documentation
+ href: documentation/
+
+- name: API
+ href: api/
+ homepage: api/index.md
+
+- name: GitHub
+ href: https://github.com/nats-io/jwt.net
+
+- name: Slack
+ href: https://slack.nats.io
diff --git a/src/NATS.Jwt.sln b/src/NATS.Jwt.sln
index fa7803f..a674182 100644
--- a/src/NATS.Jwt.sln
+++ b/src/NATS.Jwt.sln
@@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\test.yml = ..\.github\workflows\test.yml
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NATS.Jwt.DocsExamples", "..\tests\NATS.Jwt.DocsExamples\NATS.Jwt.DocsExamples.csproj", "{F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,10 @@ Global
{E144211D-3549-4E78-BFC4-B5BF7E70AEDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E144211D-3549-4E78-BFC4-B5BF7E70AEDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E144211D-3549-4E78-BFC4-B5BF7E70AEDA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9B613C47-922B-4AA8-82D7-1520A13E0F74} = {F3E7EFAD-B3EA-45F4-907B-F3C2338771CE}
@@ -71,5 +77,6 @@ Global
{1EB01F81-8857-4646-B8BF-16024F357506} = {03E5999C-5D20-4EA2-B8C8-53ACE65B221F}
{9C98B59B-D0C0-4F2B-8333-2242ED1B5F70} = {03E5999C-5D20-4EA2-B8C8-53ACE65B221F}
{03E5999C-5D20-4EA2-B8C8-53ACE65B221F} = {1F24478C-D5CB-4A58-A74E-6371F7F95C01}
+ {F6EC8BE3-17D7-4857-81B3-C3DAAF9DEF4F} = {79B0CD3B-C106-44A2-9A2A-CFDA69A3016A}
EndGlobalSection
EndGlobal
diff --git a/tests/NATS.Jwt.DocsExamples/IntroPage.cs b/tests/NATS.Jwt.DocsExamples/IntroPage.cs
new file mode 100644
index 0000000..f11de5c
--- /dev/null
+++ b/tests/NATS.Jwt.DocsExamples/IntroPage.cs
@@ -0,0 +1,101 @@
+using NATS.Client.Core;
+using NATS.NKeys;
+
+namespace NATS.Jwt.DocsExamples;
+
+public class IntroPage
+{
+ public static async Task Run()
+ {
+ Console.WriteLine("____________________________________________________________");
+ Console.WriteLine("NATS.Jwt.DocsExamples.IntroPage");
+
+ {
+ #region nats-jwt
+
+ var jwt = new NatsJwt();
+
+ // create an operator key pair (private key)
+ var okp = KeyPair.CreatePair(PrefixByte.Operator);
+ var opk = okp.GetPublicKey();
+
+ // create an operator claim using the public key for the identifier
+ var oc = jwt.NewOperatorClaims(opk);
+ oc.Name = "Example Operator";
+
+ // add an operator signing key to sign accounts
+ var oskp = KeyPair.CreatePair(PrefixByte.Operator);
+ var ospk = oskp.GetPublicKey();
+
+ // add the signing key to the operator - this makes any account
+ // issued by the signing key to be valid for the operator
+ oc.Operator.SigningKeys = [ospk];
+
+ // self-sign the operator JWT - the operator trusts itself
+ var operatorJwt = jwt.EncodeOperatorClaims(oc, okp);
+
+ // create an account keypair
+ var akp = KeyPair.CreatePair(PrefixByte.Account);
+ var apk = akp.GetPublicKey();
+
+ // create the claim for the account using the public key of the account
+ var ac = jwt.NewAccountClaims(apk);
+ ac.Name = "Example Account";
+
+ var askp = KeyPair.CreatePair(PrefixByte.Account);
+ var aspk = askp.GetPublicKey();
+
+ // add the signing key (public) to the account
+ ac.Account.SigningKeys = [aspk];
+ var accountJwt = jwt.EncodeAccountClaims(ac, oskp);
+
+ // now back to the account, the account can issue users
+ // need not be known to the operator - the users are trusted
+ // because they will be signed by the account. The server will
+ // look up the account get a list of keys the account has and
+ // verify that the user was issued by one of those keys
+ var ukp = KeyPair.CreatePair(PrefixByte.User);
+ var upk = ukp.GetPublicKey();
+ var uc = jwt.NewUserClaims(upk);
+
+ // since the jwt will be issued by a signing key, the issuer account
+ // must be set to the public ID of the account
+ uc.User.IssuerAccount = apk;
+ var userJwt = jwt.EncodeUserClaims(uc, askp);
+
+ // the seed is a version of the keypair that is stored as text
+ var userSeed = ukp.GetSeed();
+
+ var conf = $$"""
+ operator: {{operatorJwt}}
+
+ resolver: MEMORY
+ resolver_preload: {
+ {{apk}}: {{accountJwt}}
+ }
+ """;
+
+ // generate a creds formatted file that can be used by a NATS client
+ const string credsPath = $"example_user.creds";
+ await File.WriteAllTextAsync(credsPath, jwt.FormatUserConfig(userJwt, userSeed));
+
+ // now we are going to put it together into something that can be run
+ // we create a file to store the server configuration, the creds
+ // file and a small program that uses the creds file
+ const string confPath = $"example_server.conf";
+ await File.WriteAllTextAsync(confPath, conf);
+
+ // run the server:
+ // > nats-server -c example_server.conf
+
+ // Connect as user
+ var serverUrl = "nats://localhost:4222";
+ var authOpts = new NatsAuthOpts { CredsFile = credsPath };
+ var opts = new NatsOpts { Url = serverUrl, AuthOpts = authOpts };
+ await using var nats = new NatsConnection(opts);
+ await nats.PingAsync();
+
+ #endregion
+ }
+ }
+}
diff --git a/tests/NATS.Jwt.DocsExamples/NATS.Jwt.DocsExamples.csproj b/tests/NATS.Jwt.DocsExamples/NATS.Jwt.DocsExamples.csproj
new file mode 100644
index 0000000..d5c22c6
--- /dev/null
+++ b/tests/NATS.Jwt.DocsExamples/NATS.Jwt.DocsExamples.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/NATS.Jwt.DocsExamples/Program.cs b/tests/NATS.Jwt.DocsExamples/Program.cs
new file mode 100644
index 0000000..6c1e0a8
--- /dev/null
+++ b/tests/NATS.Jwt.DocsExamples/Program.cs
@@ -0,0 +1,2 @@
+
+await NATS.Jwt.DocsExamples.IntroPage.Run();