Git Product home page Git Product logo

hyper-nostr's Introduction

Hyper-Nostr Relay

Support me!

Join the community!

Usage

The goal of this tool is to behave as a public relay; think of the chosen topic as a public relay, where you can send and receive notes from your peers!

  1. Install: npm install -g hyper-nostr
  2. Run: hyper-nostr [port [...starting topics]] (default 3000)
    • Example: hyper-nostr 3000 nostr
  3. Add your relay as ws://localhost:[port]/[topic] in your Nostr client (I am using nostr as a topic to make some kind of generic swarm) (topic is now optional; if you left it blank, it goes to nostr)
  4. Setup done!

HTTPS

The best way is to setup a reverse proxy. I use caddy, so then all I need to do is to run caddy reverse-proxy --to localhost:[port]. Then I can add wss://localhost as a relay.

Browsers have the tendency to refuse self signed certificates. A workaround is to go to the reverse-proxy link https://localhost and "accept the risk". There is no page in that path, so you will see a blank page, but after that your browser client probably will accept it.

How it works

Hyper-Nostr is a distributed nostr relay that syncs your relay storage and real time events through the Hyperswarm, linearizes the databases with Autobase, and uses a Hyperbeedee database (loosely based on MongoDB).

The hyperswarm and cores management was highly abstracted thanks to Hyper SDK.

NIPs implemented

  • NIP-01 (mandatory nostr implementation)
  • NIP-02 (contact lists)
  • NIP-04 (direct messages)
  • NIP-09 (event deletion)
  • NIP-11 (relay information)
  • NIP-12 (generic tag queries)
  • NIP-16 (event treatment)
  • NIP-20 (command results)
  • NIP-33 (parametrized replaceable events)
  • NIP-45 (event counts)
  • NIP-50 (search)

Code API

import * as SDK from 'hyper-sdk'
/** (sdk: SDK.SDK, topic: string) => swarm object */
import createSwarm from 'hyper-nostr'
import goodbye from 'graceful-goodbye'

const yourStorageFolder = '.hyper-nostr-relay' // set to false to not persist
const theTopic = 'nostr'

const sdk = SDK.create({
    storage: yourStorageFolder
})
goodbye(_ => sdk.close())

const { 
    subscriptions, // a Map<subscriptionId: string, { filters: Filter[], socket: WebSocket, receivedEvents: Set<id: Number> }> object
    sendEvent, // (event: Event) => document: Object | Error | void; to send an Nostr Event to the peers and the local database.
    queryEvents, // (filters: Filter[]) => Promise<Event[]>; to query the database for the events that match the list of filters 
    sendQueryToSubscription, // (sub: Subscription, key: subscriptionId, opts: { hasLimit: Boolean }) => Promise<void> // Write the events to the socket; internally includes each id on receivedEvents and dont send duplicated events
    update // () => Promise<void>; manually sync the databases in the background
} = await createSwarm(sdk, theTopic)

Server API

The client can send the following events through the websocket:

  • REQ: Request and subscription event
    • Format: ["REQ", <subscription id>, <filters JSON>...]
    • The server then adds the socket and the filters to the subs map
    • The server will send all the events that are on the database that matches the query, followed by a ["EOSE", <subscription id>] event, signalling that all events from now on will be on real time
  • EVENT: Send an event to the relay
    • Format: ["EVENT", <event JSON>]
    • The server will use sendEvent to broadcast the event, and received events through this broadcast are internally validated and sent through the subs Map
    • The server confirms that the message was sent with an ["OK", <event id>, true, ""] (NIP-20)
  • CLOSE: Cancel a subscription
    • Format: ["CLOSE", <subscription id>]
    • Cancels a subscription, removing it from the subs map
  • COUNT: Counts the number of events that match a query (NIP-45)
    • Format: ["COUNT", <subscription id>, <filters JSON>...]
    • Query and count events that match the filters sent in the same event

The server sends the following events:

  • EOSE and OK specified above;
  • EVENT: Sending an event that matches the filters of a subscription
    • Format: ["EVENT", <subscription id>, <event JSON>]
  • NOTICE: Reporting errors
    • Format: ["NOTICE", <message>]
    • The only Notice this server implements is "Unrecognized event", for when there is no match for the event kind sent.

License

MIT

hyper-nostr's People

Contributors

ruulul avatar fernandolguevara avatar spiritbroski avatar

Stargazers

Nate avatar Luke avatar hzrd149 avatar Benz Muircroft avatar Daniel Webert avatar Melvin Carvalho avatar Sean Zellmer avatar franzap avatar Alex Siman avatar  avatar Elizabeth Marston avatar  avatar  avatar Doug Holton avatar AK avatar TheSameCat avatar  avatar  avatar Heri Sim avatar Sandalots avatar fiatjaf_ avatar  avatar Fontaine avatar rabble avatar Jonathan Brumley avatar Per Guth avatar Robby Ferguson avatar @RandyMcMillan avatar Marks avatar 22388o⚡️  avatar hasundue avatar

Watchers

@RandyMcMillan avatar  avatar  avatar arbadacarba avatar

hyper-nostr's Issues

TypeError: Cannot read properties of undefined (reading 'bitfield')

npm i hyper-nostr
/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:108
    const bitfield = core.core.bitfield
                               ^

TypeError: Cannot read properties of undefined (reading 'bitfield')
    at TreeNode.preload (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:108:32)
    at new TreeNode (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:101:10)
    at BlockEntry.getTreeNode (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:297:12)
    at Batch.getRoot (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:674:52)
    at async Batch._get (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:751:16)
    at async Batch.get (/home/benz/Desktop/nostr/node_modules/hyperbee/index.js:738:14)
    at async Autodeebee.get (/home/benz/Desktop/nostr/node_modules/hyperdeebee/autodeebee.js:65:12)
    at async Collection.indexExists (/home/benz/Desktop/nostr/node_modules/hyperdeebee/index.js:244:20)
    at async Collection.createIndex (/home/benz/Desktop/nostr/node_modules/hyperdeebee/index.js:218:20)
    at async createDB (file:///home/benz/Desktop/nostr/node_modules/hyper-nostr/db.js:19:3)

For the code:

;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = (await import('hyper-nostr')).default;
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');
  

  const yourStorageFolder = './missed-events';
  
  const sdk = await SDK.create({
    storage: yourStorageFolder,
    autoJoin: true
  });
  goodbye(_ => sdk.close());
  
  const bob = await createSwarm(sdk, 'missed-events'); // <--- error is here 
})();

📒 Cook Book - Problem 3: Sharding Large Datasets

Please note that this code is more than likely totally wrong (Until updated/fixed by @5-142857 as discussed on discord)

NIPs

For a full list of the Nostr protocol NIPS (Nostr Implementation Protocols) see: nostr-protocol/nips

📒 Sharding Large Data Sets

P2p applications are great! But, due to there lack of a central sever, users would have to hold all the needed data themselves. In this case it's something that is expected to grow and grow - it's a dictionary! This can grow so large that we will separate words into groups beginning with aa, ab, ac, ect, ba, bb, bc, ect ... So, each user can hold only the parts of the data set that they need and they can drop parts that they don't need at the moment. This is where hyper-nostr comes to the rescue with sharding!

Other assumptions

(Should be clarified)

  • This is possible (?)
  • If the user is the only one who is keeping a shard, but they want to drop it then ??? happens (?)
  • The object from createSwarm holds the publicKey (?)
;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = (await import('hyper-nostr')).default;
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');

  const yourStorageFolder = './dictionary';
  
  const sdk = await SDK.create({
      storage: yourStorageFolder,
      autoJoin: true
  });
  goodbye(_ => sdk.close());
  
  const dictionary = await createSwarm(sdk, 'dictionary');
  const dictionary.priv = generatePrivateKey();
  const dictionary.pub = getPublicKey(dictionary.priv);

  dictionary.sendEvent('REQ', 'ac', {}); // get the shard with only words that start with 'ac'

  await dictionary.update();

  let ev = {
    kind: 1000, // normal (see nip 1)
    created_at: (+new Date() / 1000),
    pubkey: dictionary.pub, 
    word: 'acid',
    definition: 'An acid is a molecule or ion capable of either donating a proton, known as a Brønsted–Lowry acid, or forming a covalent bond with an electron pair, known as a Lewis acid. The first category of acids are the proton donors, or Brønsted–Lowry acids.',
    tags: []
  };

  ev.id = getEventHash(ev);
  ev.sign = signEvent(ev, dictionary.priv);

  dictionary.sendEvent('EVENT', ev); 
  await dictionary.update();

  dictionary.sendEvent('REQ', 'mo', {});  // get the shard with only words that start with 'mo'

  const result = await dictionary.queryEvents({word: 'moose'});

  console.log(result);

  /* // has more than one result
  [
    {
      kind: 1000, // normal (see nip 1)
      created_at: 1234567890000,
      publicKey: 'tvf76dru6r76r7f', 
      word: 'moose',
      definition: 'The moose or elk is the only species in the genus Alces. The moose is the tallest and second-largest land mammal in North America, only falling short of the American buffalo in terms of mass. It is the largest and heaviest extant species of deer.',
      tags: []
    },
    {
      kind: 1000, // normal (see nip 1)
      created_at: 1234567890000,
      publicKey: '675r7vr75eww',
      word: 'moose',
      definition: '(Alces alces), largest member of the deer family Cervidae (order Artiodactyla). Moose are striking in appearance because of their towering size, black colour, long legs, pendulous muzzle, and dangling hairy dewlap (called a bell) and the immense, wide, flat antlers of old bulls.',
      tags: []
    }
  ]
  */
  
  dictionary.sendEvent('CLOSE', 'mo'); // we don't need 'mo' shard at the moment on our computer

})();

📒 Cook Book - Learning: Hyper-nostr API

Please note that this code is more than likely totally wrong (Until updated/fixed by @5-142857 as discussed on discord)

NIPs

For a full list of the Nostr protocol NIPS (Nostr Implementation Protocols) see: nostr-protocol/nips

📒 Hyper-nostr API

A full list of the hyper-nostr API

Send A REQ Event To Subscribe To A Subscription

const me = await createSwarm(sdk, 'topic');

me.sendEvent('REQ', 'news', {});

# or with filters

me.sendEvent('REQ', 'news', {
  id: '<32-bytes lowercase hex-encoded sha256 of the serialized event data>', // see nostr-tools module
  pubkey: '<32-bytes lowercase hex-encoded public key of the event creator>', // see nostr-tools module
  created_at: '<unix timestamp in seconds>',
  kind: '<integer between 0 and 65535>',
  tags: [
    ['<arbitrary string>...'],
    ...
  ],
  content: '<arbitrary string>',
  sig: '<64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>' // see nostr-tools module
});

Send A CLOSE Event To Unsubscribe From A Subscription

const me = await createSwarm(sdk, 'topic');

me.sendEvent('CLOSE', 'news');

Send An EVENT:

;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = (await import('hyper-nostr')).default;
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');

  const yourStorageFolder = './test';
  
  const sdk = await SDK.create({
      storage: yourStorageFolder,
      autoJoin: true
  });
  goodbye(_ => sdk.close());
  
  const me = await createSwarm(sdk, 'test');

  await me.update();

  const userPrivK = generatePrivateKey(); // or string from previous generation
  const userPubK = getPublicKey(userPrivK);

  let event = {
    kind: 1000, // normal (see nip 1)
    content: 'hello', // can be json?
    created_at: Math.floor(+new Date() / 1000), // seconds
    pubkey: userPubK,
    tags: []
  };

  event.id = getEventHash(deleteEvent);
  event.sign = signEvent(deleteEvent, userPrivK);

  await me.sendEvent('EVENT', event);
  await me.update();

})();

Delete An Event:

;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = (await import('hyper-nostr')).default;
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');

  const yourStorageFolder = './test';
  
  const sdk = await SDK.create({
      storage: yourStorageFolder,
      autoJoin: true
  });
  goodbye(_ => sdk.close());
  
  const me = await createSwarm(sdk, 'test');

  await me.update();

  const userPrivK = generatePrivateKey(); // or string from previous generation
  const userPubK = getPublicKey(userPrivK);

  let deleteEvent = await me.queryEvents({id: 'xyz'});

  deleteEvent = {
    kind: 5, // this means delete (see nip 9)
    content: '',
    created_at: Math.floor(+new Date() / 1000), // seconds
    pubkey: userPubK,
    tags: [
      ['e', deleteEvent.id]
    ]
  };

  deleteEvent.id = getEventHash(deleteEvent);
  deleteEvent.sign = signEvent(deleteEvent, userPrivK);

  await me.sendEvent('EVENT', deleteEvent);
  await me.update();

})();

List Subscriptions

???

Query Events

???

Send A Query To Subscriptions

What is this for ?

Update Your Database To Sync To/From The Network

const me = await createSwarm(sdk, 'test');
await me.update();

📒 Cook Book - Problem 1: Missed Messages

Please note that this code is more than likely totally wrong (Until updated/fixed by @5-142857 as discussed on discord)

NIPs

For a full list of the Nostr protocol NIPS (Nostr Implementation Protocols) see: nostr-protocol/nips

📒 Missed Messages

Problem:
In a p2p application with no central sever, any time a user is online, they will see messages meant for them but, they may not always be online. This example assumes a user is offline and would otherwise miss their messages if it were not for the implementation of hyper-nostr

Conclusion:
Nostr does not miss messages for offline users but, p2p apps without hyper-nostr do!

Other assumptions

(Should be clarified)

  • A topic is different to a subscription (?)
  • I have used the REQ and CLOSE event's properly (?)
  • Events should be deleted after they are no-longer needed or everyone's computers will fill up (? lol)
  • JSON objects inside events (?)
;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = await import('hyper-nostr');
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');

  const yourStorageFolder1 = './missed-events1';
  const yourStorageFolder2 = './missed-events1';
  
  const sdk1 = await SDK.create({
    storage: yourStorageFolder1,
    autoJoin: true
  });
  
  const sdk2 = await SDK.create({
    storage: yourStorageFolder2,
    autoJoin: true
  });
  
  goodbye(() => {
    sdk1.close();
    sdk2.close();
  });
  
  const bob = await createSwarm(sdk1, 'missed-events'); // an online user bob, who knows that alice is offline
  bob.priv = generatePrivateKey();
  bob.pub = getPublicKey(bob.priv);
  
  
  let ev = {
    kind: 1000, // regular (see nip 1)
    created_at: Math.floor(+new Date() / 1000), // seconds
    pubkey: bob.pub,
    content: {
      type: 'payment',
      currency: 'usd',
      amount: '59.74043',
      for: 'art shop painting 63'
    },
    tags: [
      ['p', 'alice']
    ]
  };
  
  ev.id = getEventHash(ev);
  ev.sign = signEvent(ev, bob.priv);
  
  bob.sendEvent('EVENT', ev);

  await bob.update();

  console.log(await bob.queryEvents([{ '#p': ['alice'] }]));






  // ... later alice comes back online ...

  const alice = await createSwarm(sdk2, 'missed-events');
  alice.priv = generatePrivateKey();
  alice.pub = getPublicKey(alice.priv);

  await alice.update();

  let result = await alice.queryEvents([{ '#p': ['alice'] }]);
  
  // alice sees this:
  /*
  [ // a list
    {
      id: '57e75e7de7e75e65yghfh546',
      kind: 1000, // regular (see nip 1)
      created_at: 1239455461100,
      pubkey: 'd75d7dutdy5ehtbgeguyjgfu'
      content: {
        type: 'payment',
        currency: 'usd',
        amount: '59.74043',
        for: 'art shop painting 63'
      },
      tags: [
        ['p', 'alice']
      ]
    }
  ]
  */

  ev = {
    kind: 5, // delete (see nip 9)
    content: '',
    created_at: Math.floor(+new Date() / 1000), // seconds
    pubkey: alice.pub,
    tags: [
      ['e', result[0].id]
    ]
  };
  
  ev.id = getEventHash(ev);
  ev.sign = signEvent(ev, alice.priv);
  
  alice.sendEvent('EVENT', ev);
  
  await alice.update();

})();

improve readme

just leaving this boilerplate here, not sure how accurate it is:

Hyper-Nostr Relay

Hyper-Nostr Relay is a WebSocket-based relay server for creating and managing distributed swarms using the Hyper SDK. It allows you to send and receive events in a decentralized manner with ease.

Features

  • WebSocket support for easy integration with web applications
  • Automatic creation and management of swarms with a provided topic
  • Handling of various message types like EVENT, REQ, CLOSE, and COUNT
  • Graceful shutdown using the graceful-goodbye module
  • Built-in support for the Hyper SDK

Getting Started

To get started, you can run the Hyper-Nostr Relay server with the desired port and an optional list of starting topics:

$ hyper-nostr [port] [startingTopics...]

For example:

$ hyper-nostr 3000 topic1 topic2

WebSocket API

Clients can connect to the Hyper-Nostr Relay server using WebSockets and send messages with the following structure:

  • ['EVENT', value]: Send an event with the given value
  • ['REQ', value, ...rest]: Request events matching the given filters (specified in rest)
  • ['CLOSE', value]: Close the subscription with the given value
  • ['COUNT', value, ...rest]: Request the count of events matching the given filters (specified in rest)

Upon receiving messages from clients, the server will respond with appropriate messages:

  • ['OK', eventId, true, '']: Acknowledgment of a sent event
  • ['EVENT', value, event]: Event data matching a subscription
  • ['EOSE', value]: End of a subscription's event stream
  • ['COUNT', value, { count: eventCount }]: Count of events matching the given filters
  • ['NOTICE', 'Unrecognized event']: Notification of an unrecognized event type

License

?

📒 Cook Book - Problem 0: Custom Isolated App Use

Please note that this code is more than likely totally wrong (Until updated/fixed by @5-142857 as discussed on discord)

NIPs

For a full list of the Nostr protocol NIPS (Nostr Implementation Protocols) see: nostr-protocol/nips

📒 Custom Isolated App Use

Most developers using this implementation of the Nostr protocol will want to use it to build social media applications that rival Twitter and Mastadon, whereas; hyper-nostr has wide ranging, broader use cases as a tool in standalone p2p app development (where the app itself may be relying on it's own internal app users to connect data in ways that a simple hypercore/autobase lacks).

Use Cases

  • A multi-writer database on the swarm
  • A shared index for quickly marrying data that a user does not personally have, to an id of known data
  • A shared cache layer
  • A shared memory for offline users, who would otherwise miss application-level events meant for them
  • Sharding large datasets
  • Data ownership

Reasoning

autobase falls short in linking literally everyone automatically together on a shared object due to it's single writer nature. Not to mention that It needs the developer to know and manage adding writers. Now, understand that hyper-nostr can be used to solve these problems, by working alongside autobase and being the needed shared multi-writer object! 😉

  • autobase is a tool for making chats and versioning systems like git collaborations and shared activities like writing music together
  • hyper-nostr replaces the role of central server (plus it can do chats / versioning / collab another way)

To Do

In /hyper-nostr/swarm.js can you please add this update?

const prefix = 'hyper-nostr-'

export default async function createSwarm (sdk, _topic, opts) {
  if (!opts) opts = {}; 
  const topic = (opts.prefix) ? opts.prefix + _topic : prefix + _topic
  ...

So that developers can use this module privately with their users (optionally) like:

createSwarm(sdk, topic); // normal hyper-nostr- known to any users on nostr
// or
createSwarm(sdk, topic, { prefix: '' }); // totally isolated to the current app
// or
createSwarm(sdk, topic, { prefix: 'myapp-' }); // isolated to a network namespace on any apps who know the namespace
// or
createSwarm(sdk, topic, { prefix: `nike-${ authKey }-` }); // protected by the nike network apps
// or
createSwarm(sdk, topic, { prefix: `${aes.decode('71a936f2a1e6bbad750933436343519983d34eecfef00d7a97b32b1103dbb0db')}-` }); // known by none / hard-coded

This will optionally allow them to isolate their apps away from the default, public hyper-nostr- discovery!

📒 Cook Book - Problem 2: Getting Unknown Users

Please note that this code is more than likely totally wrong (Until updated/fixed by @5-142857 as discussed on discord)

NIPs

For a full list of the Nostr protocol NIPS (Nostr Implementation Protocols) see: nostr-protocol/nips

📒 Getting Unknown Users

A user is about to join a p2p app, but the app needs to check if bob's sponsor exists. Due to the nature of p2p applications, there is no central server, so how does a user check for users they have not peered with before? This is where hyper-nostr helps out, with a list that everyone syncs of all users

Other assumptions

(Should be clarified)

  • The objects are just in a big array, instead, this is not a key value store (?)
  • There is no subscription here, as it's just a giant list (?)
;(async function(){
  const SDK = await import("hyper-sdk");
  const createSwarm = (await import('hyper-nostr')).default;
  const { generatePrivateKey, getPublicKey, getEventHash, signEvent } = require('nostr-tools');
  const goodbye = require('graceful-goodbye');

  const yourStorageFolder = './all-users';
  
  const sdk = await SDK.create({
      storage: yourStorageFolder,
      autoJoin: true
  });
  goodbye(_ => sdk.close());
  
  const bob = await createSwarm(sdk, 'all-users');
  const bob.priv = generatePrivateKey();
  const bob.pub = getPublicKey(bob.priv);

  let ev = {
    kind: 30000,  // keep only latest (see nip 1)
    created_at: (+new Date() / 1000),
    pubkey: bob.pub,
    content: 'bob',
    tags: []
  };
  ev.id = getEventHash(ev);
  ev.sign = signEvent(ev, bob.priv);

  bob.sendEvent('EVENT', ev); // bob joins the shared list of users

  await bob.update();

  // bob's sponsor is going to be alice! Let's check the db to see if alice is really a user ...
  
  let sponsor = await bob.queryEvent({ content: 'alice' });

  if (sponsor) {
    console.log(sponsor); // yay!
    /*
    {
      kind: 30000, // keep only latest (see nip 1)
      created_at: 1234567890000,
      pubkey: 'r6d0ds6wtyt4y4gtydrt',
      content: 'alice',
      tags: []
    }
    */
  }
  else {
    console.log('No alice here! Please choose a different sponsor.');
  }

})();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.