Usage
This chapter covers how to use the SlayOps JavaScript library to integrate OpenPGP operations into your website.
Live Demo
You can try out all the features described in this chapter using the live demo at https://localpgp.org/demo.html.
Before using the demo:
- Make sure you have the LocalPGP extension installed (see Installation)
- Allow the website
https://localpgp.orgin the extension (click the extension icon and click “Allow”)
Using the Demo Page
The demo page at https://localpgp.org/demo.html provides a complete interface to test all OpenPGP operations. Here’s how to use each section:
Step 1: Connect to the Extension
When you first load the demo page, you’ll see a status bar at the top showing the connection status:
- “Chrome extension not responding” - The website is not yet allowed. Click the LocalPGP extension icon in your browser toolbar and click “Allow” to grant access.
- “Connected” (with green indicator) - You’re connected and ready to use OpenPGP operations.
Click the Connect button to establish a connection with the extension.
Step 2: Key Management
The Key Management section lets you browse your GPG keyring:
- Click All to list all keys in your keyring
- Use the search box to find specific keys by email, name, or fingerprint
- Click Search to filter keys
The available keys will be displayed with their user IDs and fingerprints. You can click on a key to use its fingerprint in other operations.
Step 3: Encrypt a Message
In the Encrypt section:
- Enter the recipient’s fingerprint or email in the “Recipient Key” field
- Type your secret message in the “Message to Encrypt” textarea
- Optionally check “Sign while encrypting” to also sign the message
- Click 🔒 Encrypt
The encrypted PGP message will appear in the “Encrypted Output” area. You can copy this to send securely.
Step 4: Decrypt a Message
In the Decrypt section:
- Paste a PGP encrypted message in the textarea
- Click 🔓 Decrypt
- If you have the corresponding private key, the decrypted message will appear
You can also click ← Paste from Encrypt to quickly copy the output from the Encrypt section.
Note: If the message was encrypted to a key on a hardware device (like Yubikey), you’ll be prompted to enter your PIN.
Step 5: Sign a Message
In the Sign section:
- Optionally enter a specific signing key fingerprint (leave empty to use your default key)
- Type the message you want to sign
- Choose the signature type:
- Clearsign - The message and signature are combined (readable message)
- Detached - Only the signature is output (message stays separate)
- Click ✍️ Sign
The signed message or signature will appear in the output area.
Step 6: Verify a Signature
In the Verify section:
- Choose the verification mode:
- Clearsigned - For messages that include the signature
- Detached Signature - For separate message and signature
- Paste the clearsigned message OR the original message and signature
- Click ✓ Verify
The verification result will show whether the signature is valid and who signed it.
Additional Features
- Get Default Key - Shows your default GPG signing key
- Browser Info - Displays detected browser type and connection details
Quick Start
import { SlayOps } from 'slayops';
// Create instance
const pgp = new SlayOps();
// Connect to the extension
await pgp.connect();
// Now you can use OpenPGP operations
const keys = await pgp.getKeys();
console.log('Available keys:', keys);
Or with a CDN:
<script src="https://unpkg.com/slayops/dist/slayops.js"></script>
<script>
const pgp = new SlayOps.SlayOps();
async function init() {
await pgp.connect();
const keys = await pgp.getKeys();
console.log('Available keys:', keys);
}
init();
</script>
Configuration
The SlayOps constructor accepts configuration options:
const pgp = new SlayOps({
timeout: 30000, // Request timeout in ms (default: 30000)
autoConnect: false, // Auto-connect on creation (default: false)
});
For Development
If you’re developing with a local unpacked extension, you can specify the extension ID:
// Option 1: Single dev ID
const pgp = new SlayOps({ chromeExtensionId: 'your-local-dev-id' });
// Option 2: Try multiple IDs (production first, then dev)
const pgp = new SlayOps({
chromeExtensionIds: [
'ckgehekhpgcaaikpadklkkjgdgoebdnh', // Production
'your-local-dev-id' // Dev fallback
]
});
Connection Management
Connecting to the Extension
const pgp = new SlayOps();
// Connect to extension
await pgp.connect();
// Check if connected
if (pgp.isConnected()) {
console.log('Connected to LocalPGP');
}
// Get connection status
const status = pgp.getStatus();
// Returns: 'disconnected' | 'connecting' | 'connected' | 'error'
// Check if extension is available
if (pgp.isExtensionAvailable()) {
console.log('Extension is installed');
}
// Get human-readable status message
console.log(pgp.getStatusMessage());
Events
SlayOps emits events you can listen to:
pgp.on('connected', () => {
console.log('Connected to extension!');
});
pgp.on('disconnected', () => {
console.log('Disconnected from extension');
});
pgp.on('error', (err) => {
console.error('Error:', err);
});
pgp.on('ready', () => {
console.log('Extension is ready');
});
// Remove a listener
pgp.off('connected', handler);
Key Management
Listing Keys
// Get all keys
const allKeys = await pgp.getKeys();
// Search for keys by email, name, or fingerprint
const searchResults = await pgp.getKeys('alice@example.com');
// Get only secret keys (keys you can sign/decrypt with)
const secretKeys = await pgp.getKeys('', true);
Getting the Default Key
const defaultKey = await pgp.getDefaultKey();
if (defaultKey) {
console.log('Default key:', defaultKey.fingerprint);
console.log('User IDs:', defaultKey.userids);
}
Importing Keys
const armoredPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----`;
await pgp.importKey(armoredPublicKey);
console.log('Key imported successfully');
Encryption
Basic Encryption
// Encrypt a message for a recipient
const encrypted = await pgp.encrypt(
'Hello, this is a secret message!',
['recipient@example.com'] // Can be email, name, or fingerprint
);
console.log(encrypted);
// Output: -----BEGIN PGP MESSAGE-----...
Encrypt for Multiple Recipients
const encrypted = await pgp.encrypt(
'Secret message for the team',
[
'alice@example.com',
'bob@example.com',
'5286C32E7C71E14C4C82F9AE0B207108925CB162' // Fingerprint
]
);
Encrypt with Signing
const encrypted = await pgp.encrypt(
'Signed and encrypted message',
['recipient@example.com'],
{
sign: true, // Sign while encrypting
signingKey: 'FINGERPRINT', // Optional: specific signing key
alwaysTrust: true // Trust recipients without verification
}
);
Decryption
Basic Decryption
const encryptedMessage = `-----BEGIN PGP MESSAGE-----
...
-----END PGP MESSAGE-----`;
const result = await pgp.decrypt(encryptedMessage);
console.log('Decrypted:', result.data);
// If the message was signed
if (result.signatures && result.signatures.length > 0) {
console.log('Signed by:', result.signatures[0].fingerprint);
console.log('Signature valid:', result.signatures[0].valid);
}
Signing
Clearsign (Message + Signature Together)
// Default: clearsign mode
const signed = await pgp.sign('This is my message');
console.log(signed);
// Output:
// -----BEGIN PGP SIGNED MESSAGE-----
// Hash: SHA256
//
// This is my message
// -----BEGIN PGP SIGNATURE-----
// ...
// -----END PGP SIGNATURE-----
Detached Signature
const signature = await pgp.sign('This is my message', {
mode: 'detached'
});
console.log(signature);
// Output: -----BEGIN PGP SIGNATURE-----...
Sign with Specific Key
const signed = await pgp.sign('My message', {
signingKey: '5286C32E7C71E14C4C82F9AE0B207108925CB162'
});
Verification
Verify Clearsigned Message
const clearsignedMessage = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
This is my message
-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`;
const result = await pgp.verify(clearsignedMessage);
console.log('Valid:', result.isValid);
console.log('Signatures:', result.signatures);
// Each signature contains:
// - fingerprint: Signer's key fingerprint
// - valid: Whether the signature is valid
// - status: Detailed status information
Verify Detached Signature
const originalMessage = 'This is my message';
const detachedSignature = `-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`;
const result = await pgp.verify(originalMessage, detachedSignature);
console.log('Valid:', result.isValid);
Complete Example
Here’s a complete example showing a typical workflow:
<!DOCTYPE html>
<html>
<head>
<title>SlayOps Demo</title>
<script src="https://unpkg.com/slayops/dist/slayops.js"></script>
</head>
<body>
<h1>OpenPGP Demo</h1>
<div id="status">Connecting...</div>
<div id="keys"></div>
<h2>Encrypt</h2>
<textarea id="message" placeholder="Enter message"></textarea>
<input id="recipient" placeholder="Recipient email or fingerprint">
<button onclick="encryptMessage()">Encrypt</button>
<pre id="encrypted-output"></pre>
<h2>Sign</h2>
<textarea id="sign-message" placeholder="Enter message to sign"></textarea>
<button onclick="signMessage()">Sign</button>
<pre id="signed-output"></pre>
<script>
const pgp = new SlayOps.SlayOps();
async function init() {
try {
await pgp.connect();
document.getElementById('status').textContent = 'Connected!';
// List keys
const keys = await pgp.getKeys();
document.getElementById('keys').innerHTML =
'<h3>Available Keys:</h3>' +
keys.map(k => `<p>${k.userids[0]?.uid || 'No UID'}<br><code>${k.fingerprint}</code></p>`).join('');
} catch (err) {
document.getElementById('status').textContent = 'Error: ' + err.message;
}
}
async function encryptMessage() {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
try {
const encrypted = await pgp.encrypt(message, [recipient]);
document.getElementById('encrypted-output').textContent = encrypted;
} catch (err) {
document.getElementById('encrypted-output').textContent = 'Error: ' + err.message;
}
}
async function signMessage() {
const message = document.getElementById('sign-message').value;
try {
const signed = await pgp.sign(message);
document.getElementById('signed-output').textContent = signed;
} catch (err) {
document.getElementById('signed-output').textContent = 'Error: ' + err.message;
}
}
init();
</script>
</body>
</html>
Browser Detection
SlayOps automatically handles browser differences between Chrome and Firefox:
// Get detected browser type
const browserType = pgp.getBrowserType();
// Returns: 'chrome' | 'firefox' | 'firefox-injected' | 'chrome-no-api' | 'unknown'
// Manually trigger browser detection
pgp.detectBrowser();
Error Handling
Always wrap SlayOps calls in try-catch blocks:
try {
await pgp.connect();
const encrypted = await pgp.encrypt('message', ['recipient@example.com']);
} catch (err) {
if (err.message.includes('not responding')) {
console.error('Extension not installed or website not allowed');
} else if (err.message.includes('No matching keys')) {
console.error('Recipient key not found in keyring');
} else {
console.error('Operation failed:', err.message);
}
}
Common errors:
- Extension not responding: The extension is not installed, or the website is not in the allowed list
- No matching keys: The recipient’s public key is not in the local GPG keyring
- Decryption failed: You don’t have the private key for this message, or the message is corrupted
- Timeout: The operation took too long (e.g., waiting for hardware key PIN entry)