mirror of
https://github.com/aljazceru/contextvm-docs.git
synced 2025-12-19 15:04:24 +01:00
init
This commit is contained in:
93
src/content/docs/signer/custom-signer-development.md
Normal file
93
src/content/docs/signer/custom-signer-development.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Custom Signer Development
|
||||
description: Learn how to create a custom signer for the @contextvm/sdk.
|
||||
---
|
||||
|
||||
# Custom Signer Development
|
||||
|
||||
One of the key design features of the `@contextvm/sdk` is its modularity, which is exemplified by the [`NostrSigner`](./nostr-signer-interface) interface. By creating your own implementation of this interface, you can integrate the SDK with any key management system, such as a hardware wallet, a remote signing service (like an HSM), or a browser extension.
|
||||
|
||||
## Why Create a Custom Signer?
|
||||
|
||||
While the [`PrivateKeySigner`](./private-key-signer) is suitable for many development and server-side scenarios, a custom signer is often necessary when:
|
||||
|
||||
- **Security is paramount**: You need to keep private keys isolated from the main application logic, for example, in a hardware security module (HSM) or a secure enclave.
|
||||
- **Interacting with external wallets**: Your application needs to request signatures from a user's wallet, such as a browser extension (e.g., Alby, Noster) or a mobile wallet.
|
||||
- **Complex key management**: Your application uses a more complex key management architecture that doesn't involve direct access to raw private keys.
|
||||
|
||||
## Implementing the `NostrSigner` Interface
|
||||
|
||||
To create a custom signer, you need to create a class that implements the `NostrSigner` interface. This involves implementing two main methods: `getPublicKey()` and `signEvent()`, as well as an optional `nip44` object for encryption.
|
||||
|
||||
### Example: A NIP-07 Browser Signer (window.nostr)
|
||||
|
||||
A common use case for a custom signer is in a web application that needs to interact with a Nostr browser extension (like Alby, nos2x, or Blockcore) that exposes the `window.nostr` object according to [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md). This allows the application to request signatures and encryption from the user's wallet without ever handling private keys directly.
|
||||
|
||||
Here is how you could implement a `NostrSigner` that wraps the `window.nostr` object:
|
||||
|
||||
```typescript
|
||||
import { NostrSigner } from '@ctxvm/sdk/core';
|
||||
import { UnsignedEvent, NostrEvent } from 'nostr-tools';
|
||||
|
||||
// Define the NIP-07 window.nostr interface for type-safety
|
||||
declare global {
|
||||
interface Window {
|
||||
nostr?: {
|
||||
getPublicKey(): Promise<string>;
|
||||
signEvent(event: UnsignedEvent): Promise<NostrEvent>;
|
||||
nip44?: {
|
||||
encrypt(pubkey: string, plaintext: string): Promise<string>;
|
||||
decrypt(pubkey: string, ciphertext: string): Promise<string>;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Nip07Signer implements NostrSigner {
|
||||
constructor() {
|
||||
if (!window.nostr) {
|
||||
throw new Error('NIP-07 compatible browser extension not found.');
|
||||
}
|
||||
}
|
||||
|
||||
async getPublicKey(): Promise<string> {
|
||||
if (!window.nostr) throw new Error('window.nostr not found.');
|
||||
return await window.nostr.getPublicKey();
|
||||
}
|
||||
|
||||
async signEvent(event: UnsignedEvent): Promise<NostrEvent> {
|
||||
if (!window.nostr) throw new Error('window.nostr not found.');
|
||||
return await window.nostr.signEvent(event);
|
||||
}
|
||||
|
||||
nip44 = {
|
||||
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
||||
if (!window.nostr?.nip44) {
|
||||
throw new Error('The extension does not support NIP-44 encryption.');
|
||||
}
|
||||
return await window.nostr.nip44.encrypt(pubkey, plaintext);
|
||||
},
|
||||
|
||||
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
||||
if (!window.nostr?.nip44) {
|
||||
throw new Error('The extension does not support NIP-44 decryption.');
|
||||
}
|
||||
return await window.nostr.nip44.decrypt(pubkey, ciphertext);
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Implementing `nip44` for Decryption
|
||||
|
||||
When using a NIP-07 signer, the `nip44` implementation is straightforward, as you can see in the example above. You simply delegate the calls to the `window.nostr.nip44` object.
|
||||
|
||||
It's important to include checks to ensure that the user's browser extension supports `nip44`, as it is an optional part of the NIP-07 specification. If the extension does not support it, you should throw an error to prevent unexpected behavior.
|
||||
|
||||
## Using Your Custom Signer
|
||||
|
||||
Once your custom signer class is created, you can instantiate it and pass it to any component that requires a `NostrSigner`, such as the `NostrClientTransport` or `NostrServerTransport`. The rest of the SDK will use your custom implementation seamlessly.
|
||||
|
||||
## Next Steps
|
||||
|
||||
With the `Signer` component covered, let's move on to the **[Relay](./relay-handler-interface)**, which handles another critical aspect of Nostr communication: managing connections to relays.
|
||||
60
src/content/docs/signer/nostr-signer-interface.md
Normal file
60
src/content/docs/signer/nostr-signer-interface.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: NostrSigner Interface
|
||||
description: An interface for signing Nostr events.
|
||||
---
|
||||
|
||||
# `NostrSigner` Interface
|
||||
|
||||
The `NostrSigner` interface is a central component of the `@contextvm/sdk`, defining the standard for cryptographic signing operations. Every Nostr event must be signed by a private key to be considered valid, and this interface provides a consistent, pluggable way to handle this requirement.
|
||||
|
||||
## Purpose and Design
|
||||
|
||||
The primary purpose of the `NostrSigner` is to abstract the process of event signing. By depending on this interface rather than a concrete implementation, the SDK's transports and other components can remain agnostic about how and where private keys are stored and used.
|
||||
|
||||
This design offers several key benefits:
|
||||
|
||||
- **Security**: Private keys can be managed in secure environments (e.g., web extensions, hardware wallets, dedicated signing services) without exposing them to the application logic.
|
||||
- **Flexibility**: Developers can easily swap out the default signer with a custom implementation that meets their specific needs.
|
||||
- **Modularity**: The signing logic is decoupled from the communication logic, leading to a cleaner, more maintainable codebase.
|
||||
|
||||
## Interface Definition
|
||||
|
||||
The `NostrSigner` interface is defined in [`core/interfaces.ts`](/core/interfaces#nostrsigner).
|
||||
|
||||
```typescript
|
||||
export interface NostrSigner {
|
||||
getPublicKey(): Promise<string>;
|
||||
signEvent(event: EventTemplate): Promise<NostrEvent>;
|
||||
|
||||
// Optional NIP-04 encryption support (deprecated)
|
||||
nip04?: {
|
||||
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
||||
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
||||
};
|
||||
|
||||
// Optional NIP-44 encryption support
|
||||
nip44?: {
|
||||
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
||||
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- `getPublicKey()`: Asynchronously returns the public key corresponding to the signer's private key.
|
||||
- `signEvent(event)`: Takes an unsigned Nostr event, signs it, and returns the signature.
|
||||
- `nip04`: (Deprecated) Provides NIP-04 encryption support.
|
||||
- `nip44`: Provides NIP-44 encryption support.
|
||||
|
||||
Any class that implements this interface can be used as a signer throughout the SDK.
|
||||
|
||||
## Implementations
|
||||
|
||||
The SDK provides a default implementation for common use cases and allows for custom implementations for advanced scenarios.
|
||||
|
||||
- **[PrivateKeySigner](./private-key-signer)**: The default implementation, which takes a raw private key string and performs signing operations locally.
|
||||
- **[Custom Signer Development](./custom-signer-development)**: A guide to creating your own signer by implementing the `NostrSigner` interface.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about the default implementation: **[PrivateKeySigner](./private-key-signer)**
|
||||
- Learn how to create your own: **[Custom Signer Development](./custom-signer-development)**
|
||||
70
src/content/docs/signer/private-key-signer.md
Normal file
70
src/content/docs/signer/private-key-signer.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: PrivateKeySigner
|
||||
description: A default signer implementation for the @contextvm/sdk.
|
||||
---
|
||||
|
||||
# `PrivateKeySigner`
|
||||
|
||||
The `PrivateKeySigner` is the default implementation of the [`NostrSigner`](./nostr-signer-interface) interface provided by the `@contextvm/sdk`. It is a straightforward and easy-to-use signer that operates directly on a raw private key provided as a hexadecimal string.
|
||||
|
||||
## Overview
|
||||
|
||||
The `PrivateKeySigner` is designed for scenarios where the private key is readily available in the application's environment. It handles all the necessary cryptographic operations locally, including:
|
||||
|
||||
- Deriving the corresponding public key.
|
||||
- Signing Nostr events.
|
||||
- Encrypting and decrypting messages using NIP-44.
|
||||
|
||||
## `constructor(privateKey: string)`
|
||||
|
||||
The constructor takes a single argument:
|
||||
|
||||
- **`privateKey`**: A hexadecimal string representing the 32-byte Nostr private key.
|
||||
|
||||
When instantiated, the `PrivateKeySigner` will immediately convert the hex string into a `Uint8Array` and derive the public key, which is then cached for future calls to `getPublicKey()`.
|
||||
|
||||
## Usage Example
|
||||
|
||||
```typescript
|
||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
||||
|
||||
// Replace with a securely stored private key
|
||||
const privateKeyHex = 'your-32-byte-private-key-in-hex';
|
||||
|
||||
const signer = new PrivateKeySigner(privateKeyHex);
|
||||
|
||||
// You can now pass this signer instance to a transport
|
||||
const transportOptions = {
|
||||
signer: signer,
|
||||
// ... other options
|
||||
};
|
||||
```
|
||||
|
||||
## Key Methods
|
||||
|
||||
The `PrivateKeySigner` implements all the methods required by the `NostrSigner` interface.
|
||||
|
||||
### `async getPublicKey(): Promise<string>`
|
||||
|
||||
Returns a promise that resolves with the public key corresponding to the private key provided in the constructor.
|
||||
|
||||
### `async signEvent(event: UnsignedEvent): Promise<NostrEvent>`
|
||||
|
||||
Takes an unsigned Nostr event, signs it using the private key, and returns a promise that resolves with the finalized, signed `NostrEvent`.
|
||||
|
||||
### `nip44`
|
||||
|
||||
The `PrivateKeySigner` also provides a `nip44` object that implements NIP-44 encryption and decryption. This is used internally by the transports when encryption is enabled, and it allows the `decryptMessage` function to work seamlessly with this signer.
|
||||
|
||||
- `encrypt(pubkey, plaintext)`: Encrypts a plaintext message for a given recipient public key.
|
||||
- `decrypt(pubkey, ciphertext)`: Decrypts a ciphertext message received from a given sender public key.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
While the `PrivateKeySigner` is convenient, it requires you to handle a raw private key directly in your application code. **It is crucial to manage this key securely.** Avoid hard-coding private keys in your source code. Instead, use environment variables or a secure secret management system to load the key at runtime.
|
||||
|
||||
For applications requiring a higher level of security, consider creating a custom signer that interacts with a hardware wallet or a remote signing service.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn how to build a custom signer: **[Custom Signer Development](./custom-signer-development)**
|
||||
Reference in New Issue
Block a user