feat(readme): document built-in ACME directory server and CA capabilities

This commit is contained in:
2026-03-19 09:29:13 +00:00
parent 47168408cc
commit e570ac6db0
6 changed files with 348 additions and 192 deletions

124
readme.md
View File

@@ -1,6 +1,6 @@
# @push.rocks/smartacme
A TypeScript-based ACME client for Let's Encrypt certificate management with a focus on simplicity and power. 🔒
A TypeScript-based ACME client and server for certificate management with a focus on simplicity and power. Includes a full RFC 8555-compliant ACME client for Let's Encrypt and a built-in ACME Directory Server for running your own Certificate Authority.
## Issue Reporting and Security
@@ -372,10 +372,117 @@ await smartAcme.stop();
server.close();
```
## ACME Directory Server (Built-in CA)
SmartAcme includes a full RFC 8555-compliant ACME Directory Server, allowing you to run your own Certificate Authority. This is useful for internal PKI, development/testing environments, and air-gapped networks.
### Quick Start — ACME Server
```typescript
import { server } from '@push.rocks/smartacme';
const acmeServer = new server.AcmeServer({
port: 14000,
challengeVerification: false, // Auto-approve challenges (for testing)
caOptions: {
commonName: 'My Internal CA',
certValidityDays: 365,
},
});
await acmeServer.start();
console.log(acmeServer.getDirectoryUrl()); // http://localhost:14000/directory
console.log(acmeServer.getCaCertPem()); // Root CA certificate in PEM format
// ... use it, then shut down
await acmeServer.stop();
```
### Server Options
```typescript
interface IAcmeServerOptions {
port?: number; // Default: 14000
hostname?: string; // Default: '0.0.0.0'
baseUrl?: string; // Auto-built from hostname:port if not provided
challengeVerification?: boolean; // Default: true. Set false to auto-approve challenges
caOptions?: {
commonName?: string; // CA subject CN (default: 'SmartACME Test CA')
validityDays?: number; // Root cert validity in days (default: 3650)
certValidityDays?: number; // Issued cert validity in days (default: 90)
};
}
```
### Using the Server with SmartAcme Client
Point the SmartAcme client at your own ACME server for a fully self-contained PKI:
```typescript
import { SmartAcme, certmanagers, handlers, server } from '@push.rocks/smartacme';
// 1. Start your own CA
const acmeServer = new server.AcmeServer({
port: 14000,
challengeVerification: false,
});
await acmeServer.start();
// 2. Set up the client pointing at your CA
const memHandler = new handlers.Http01MemoryHandler();
const smartAcme = new SmartAcme({
accountEmail: 'admin@internal.example.com',
certManager: new certmanagers.MemoryCertManager(),
environment: 'integration',
challengeHandlers: [memHandler],
directoryUrl: acmeServer.getDirectoryUrl(), // Use your own CA!
});
await smartAcme.start();
const cert = await smartAcme.getCertificateForDomain('myapp.internal');
// cert.publicKey — PEM certificate chain signed by your CA
// cert.privateKey — PEM private key
await smartAcme.stop();
await acmeServer.stop();
```
### Server Endpoints
The ACME server implements all RFC 8555 endpoints:
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/directory` | GET | ACME directory with all endpoint URLs |
| `/new-nonce` | HEAD/GET | Fresh replay nonce |
| `/new-account` | POST | Account registration/lookup |
| `/new-order` | POST | Create certificate order |
| `/order/:id` | POST | Poll order status |
| `/authz/:id` | POST | Get authorization with challenges |
| `/challenge/:id` | POST | Trigger or poll challenge validation |
| `/finalize/:id` | POST | Submit CSR and issue certificate |
| `/cert/:id` | POST | Download PEM certificate chain |
### Challenge Verification
By default, the server performs real challenge verification (HTTP-01 fetches the token, DNS-01 queries TXT records). Set `challengeVerification: false` to auto-approve all challenges — useful for testing or internal environments where domain validation isn't needed.
### Root CA Certificate
Use `getCaCertPem()` to retrieve the root CA certificate for trust configuration:
```typescript
import * as fs from 'fs';
fs.writeFileSync('/usr/local/share/ca-certificates/my-ca.crt', acmeServer.getCaCertPem());
// Then: sudo update-ca-certificates
```
## Architecture
Under the hood, SmartAcme uses a fully custom RFC 8555-compliant ACME protocol implementation (no external ACME libraries). Key internal modules:
### Client Modules (`ts/acme/`)
| Module | Purpose |
|--------|---------|
| `AcmeClient` | Top-level ACME facade — orders, authorizations, finalization |
@@ -386,7 +493,20 @@ Under the hood, SmartAcme uses a fully custom RFC 8555-compliant ACME protocol i
| `AcmeChallengeManager` | Key authorization computation and challenge completion |
| `TaskManager` | Constraint-based concurrency control, rate limiting, and request deduplication via `@push.rocks/taskbuffer` |
All cryptographic operations use `node:crypto`. The only external crypto dependency is `@peculiar/x509` for CSR generation.
### Server Modules (`ts_server/`)
| Module | Purpose |
|--------|---------|
| `AcmeServer` | Top-level server facade — start, stop, configuration |
| `AcmeServerCA` | Self-signed root CA generation and certificate signing via `@peculiar/x509` |
| `JwsVerifier` | JWS signature verification (inverse of `AcmeCrypto.createJws`) |
| `NonceManager` | Single-use replay nonce generation and validation |
| `ChallengeVerifier` | HTTP-01 and DNS-01 challenge verification (with bypass mode) |
| `AcmeRouter` | Minimal HTTP router with parameterized path support |
| `MemoryAccountStore` | In-memory ACME account storage |
| `MemoryOrderStore` | In-memory order, authorization, challenge, and certificate storage |
All cryptographic operations use `node:crypto`. The only external crypto dependency is `@peculiar/x509` for CSR generation and certificate signing.
## License and Legal Information