Where communities thrive

  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
Repo info
  • Jun 24 22:15
    noctisatrae commented #1258
  • Jun 24 22:15
    noctisatrae commented #1258
  • Jun 24 14:51
    bmatusiak commented #1258
  • Jun 23 20:35
    bart commented #404
  • Jun 23 19:15
    bart commented #404
  • Jun 23 19:14
    bart commented #404
  • Jun 23 15:28
    bart commented #259
  • Jun 23 12:08
    noctisatrae commented #1024
  • Jun 23 12:05
    noctisatrae commented #1257
  • Jun 23 12:04
    noctisatrae commented #1257
  • Jun 23 00:03
    ty-ler1 commented #743
  • Jun 22 15:28
    noctisatrae commented #1258
  • Jun 22 15:25
    noctisatrae opened #1258
  • Jun 20 16:01
    Jonath-z closed #1255
  • Jun 20 14:07
    worldpeaceenginelabs updated the wiki
  • Jun 19 20:59
    pwFoo opened #1257
  • Jun 19 13:12
    mmalmi edited #1256
  • Jun 19 13:04
    mmalmi edited #1256
  • Jun 19 13:03
    mmalmi edited #1256
  • Jun 19 13:02
    mmalmi opened #1256

@despiegk i had the same problem i wrote some proxy types but its bit hacky and i dont use all functions from api so i didnt need full interface also i restricted their usage to end on first on() or once()

import Gun, { SEA } from "gun";
import 'gun/axe'
import 'gun/sea'
import { IGunConstructorOptions } from "gun/types/options";
import { AckCallback } from "gun/types/types";

type AuthArgs = [username: string, password: string, callback: (data: { err?: string }) => any] | [ pair :SEAPair, callback: (data: { err?: string }) => any]

export interface GunUser<T> {
    is?: undefined
    recall: (opt?: { sessionStorage: typeof sessionStorage }) => GunUser<T> |AuthenticatedGunUser<T>
    auth: (...t: AuthArgs) => GunUser<T> |AuthenticatedGunUser<T> 
    create: (...t: AuthArgs) => GunUser<T> |AuthenticatedGunUser<T> 

export interface AuthenticatedGunUser<T >{
    is: {
        alias: string | SEAPair
        epub: string
        pub: string
    get: <TKey extends keyof T>(name: TKey) => AuthenticatedGunUserTree<T, TKey> & Promise<T[TKey]>
    leave: () => void

type AuthenticatedGunUserTree<T  , TKey extends keyof T> = AuthenticatedGunUser<T[TKey]> & GunTree<T,TKey>;

interface GunGet<T> {
    get: <TKey extends keyof T>(name: TKey) =>  GunTree<T ,TKey> & GunGet<T[TKey]> &Promise<T[TKey]>

type GunTree<T  , TKey extends keyof T> = {
    on: (callback:(state:T[TKey], key: TKey) => any) => { off: ()=> void }
    once: (callback: (state:T[TKey], key: TKey) => any) => void
    put: (state: T[TKey], callback?: AckCallback, options ?: {opt ?: { cert ?: string}} ) => void,
    set: (state: T[TKey]) => void,
    map: (match?: any) => AuthenticatedGunUserTree<T[TKey], keyof T[TKey]>

export function isUserAuthenticated  <T>(user : GunUser<T> |AuthenticatedGunUser<T>): user is AuthenticatedGunUser<T>{
    return !!user.is

interface GunDefinition {
    user: <T>() => GunUser<T> | AuthenticatedGunUser<T>,
    get: <T,TKey extends keyof T>(name: TKey) =>  GunTree<T ,TKey> & GunGet<T[TKey]> &Promise<T[TKey]>,
    on: (eventName: 'auth', callback: () => any) => { off: ()=> void },

const TypedGun = Gun as any as {
    (options?: string | string[] | IGunConstructorOptions): GunDefinition;
    new (options?: string | string[] | IGunConstructorOptions): GunDefinition;

export interface SEAPair{
    epriv: string
    epub: string
    priv: string
    pub: string

type Authority = SEAPair

export const TypedSEA = SEA as any as {
    secret: (epubKey: string, pair: SEAPair) => Promise<string>
    pair: () => Promise<SEAPair>
    sign: (data:any, pair: SEAPair) => Promise<string>
    verify: <T>(data: string, pair: SEAPair | string) => Promise<T | undefined>
    encrypt: (data:any, pair: SEAPair | string) => Promise<string>
    decrypt: <T>(data:string, pair: SEAPair | string) => Promise<T| undefined>
    certify: (user : string | string[]| {pub:string} | {pub:string}[], policies: any, pair: Authority, callback: (cert:string) => any, opt?: { blacklist?: string, expiry?:number }) => Promise<string>

just use TypedGun or TypedSEA


@amark. Please implement this Forum as multiple threads initiated by each user, not a single thread currently. It is just a mess. Shift this Forum to reddit, and leave gitter?

@amark how can I change the username or alias for gun.user()?

@amark. I want to implement state management on the Clent-side using gundb. I want to expose only a subset of nodes e.g. gun.get("sessionid") on Client-side. I searched a lot, but still have doubts what if client-side hacking may access full gun graph network using XSS/code-injection. What security measures should I take? Is including SEA library enough security measure?

@amark. I am implementing gundb for a production grade app.

First post. Been stuck on reproducing the basic to-do list example on my desktop. Initialized the http server at local host and ran the following code. The list doesn't update or really do anything. Understand this is extremely basic, but I've been stuck for days now. Thanks!

// Clear out localstorage to give Gun a fresh start on every load

// Import Gun as a dependency
import Gun from 'gun'

var gun = Gun();

const server = require('http').createServer().listen(8080);
const gun = Gun({web: server});

var items = gun.get('items');

$('form').on('submit', function(e){

items.map().on(function(item, id){
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
} else {

Barłomiej Bąk
@shecky2000 it seems you messed up browser and server side there
you can setup your own super peer using nodejs, but for now you can use a public server
so what you need to do is
const gun = Gun( / settings /)
and you are good to go
5 replies
remember that public servers are used by many other developers and reset data every 15 min or so
Magnus Furcifer

Sorry if this is a silly question, but I've been going over the docs today and I don't quite understand conceptually how the scopes work. So I think I get how user contexts work with storing data like in the following snippet:
$('#said').on('submit', function(e){
if(!user.is){ return }

where the get/set/map methods apply to that users space, but if any endpoint can set data into the global space doesn't that mean that the global data set can be flooded? or is there some logic in the relay servers that I'm not understanding that prevents arbitrary data from being ingested and stored?

If I created an instance of gun with a public peer, is there anything stopping me just smashing it with data?
sua yoo

thoughts on how i would achieve key pair rotation for users created with user.create? reading the docs, it seems that gun users are essentially key pairs generated with SEA.pair(). does this mean i need to keep my own way of mapping usernames/alias to a pair, something like:

SEA.pair(({pair}) => {

  // use `.activePublicKey` to encrypt, certify, etc.

or will gun.user().create(oldUsername, oldPaassphrase) automatically update the keys? (nope that would return a "user already exists" error.)

Barłomiej Bąk
@SuaYoo I'm not sure what are you trying to achieve, but in general user is identified by SEA keys(), you should keep them secret, except for the public key. You can use them for encrytpion, but it's not a must, you can generate another pair of keys for encryption purposes. You can use user's keys for authentication instead of login/password. The advantage of using keys for authentication is that it does not require lookup which means it's faster than login/password way
1 reply
Barłomiej Bąk

@amark hey Mark, I guess I know why I had issue with missing data in .map().on(). It seem that 'on' cannot be async like

gun.get('docs').map().on(async data => { ... })

Maybe I'm wrong, but since I removed async from my code I have better results

@magnusfurcifer_twitter: First of all, the global space should not be used because anyone can change it or even delete it. All data must be stored in user nodes (because only that particular user can change the data by default and nobody else) or using Content Addressing (nobody can change it, not even the creator).
Second of all, if someone decides to dump a lot of data in your network, the only problem is that you will end-up storing it for them and nothing else. The apps you build will not care about that data and they won't download it on their clients. If you still want to restrict write access to the network based on some rules, you can have a loot at https://github.com/zrrrzzt/bullet-catcher but I haven't used it myself, I only know of it. The problem is that it goes against the Gun philosophy on long term where everybody should be able to donate their relays to the network and we all use and enjoy the network together (with the purpose of better redundancy, wider distribution around the world and at lower costs)
1 reply
Mark Nadal
@despiegk :clap: @MaciejDot ! Can you contribute that to the Type Definitions or ... ANYONE, literally ANYONE, help edit this page? https://github.com/amark/gun/wiki/TypeScript
@awaisnazir21 :) :wave: . Sorry, :( that makes it hard for me to answer people versus a thread. I'm more than happy for the community to use a forum, but if a question fallsback to me... it needs to be accessible for me to process. :/
I think all you have to do is gun.get('@newAliasYouWant').set(user) (where user is logged in)
XSS is sadly every app owner's responsibility to handle, tho I think I figured out a new way to fix this, and hope to be demoing a prototype soon. The Iris app does do a local-only GUN peer for indexing tho, by local = GUN(); gun = GUN(peers);. Does this help?
Congrats!! How many active users does your app currently have!?
@shecky2000 a few days! Oh noooo! THat's not good, the rule is 5min!! (partly cause sometimes it takes a day to get a reply). :clap: @dweorh_twitter was fast tho! Were you able to get it fixed? Seems like it. Remember, browsers still require :( :( bootstrapping peers to make WebRTC connections or sync. Tho we're working with browsers... (they're so slow) to maybe fix this in the future. NodeJS is able to access UDP multicast tho, so its more powerful than browsers.
@magnusfurcifer_twitter :wave: . :clap: :clap: @rococtz:matrix.org well said! Also: User space only allows the user to write to it. Public space can be written to by anyone, yes. If you're worried about that, just don't use the public space - that's fine. But its an important feature for some apps. We'll be building DDoS protection and stuff into AXE, but not there yet (the real 60 million users is DDoSing things enough, that I'm trying to scale up for, not even bots!).
@SuaYoo :clap: @dweorh_twitter tho yeah, Iris plans to enable key rotation management in the future, but even it (probably the most advanced app on this stuff so far) hasn't built it yet either. So key rotation is important, but I haven't seen it demoed yet.
@dweorh_twitter hmmm. Any JS function should be allowed to be async, this would be very mysterious if true. Can you replicate?
Mark Nadal
Hey everyone + @rogowski , important security question & help I can't find an answer to:
UntrustedApp.com -> iframe.src = TrustedApp.com -> sandboxIframe
The whole reason I was adding a trusted iframe between the sandboxIframe is to STOP/PREVENT sandboxIframe being able to talk to UntrustedApp.com, but so far... untrusted sandbox code injected into the sandbox could still postMessage to UntrustedApp.com, I need to stop this. How?
@daviddahl do you know any people who would know the answer ^?
Lorenzo Mangani @qxip
Isn't that what COOP/COEP are for?
@amark or i just can repair ts definition that is shipped with package
then there want be need for documentation types
tedd pasta
made some updates
it now shows examples
if you use gun-fetch without a special character as the first letter of the url, then it will assume you are wanting user interaction. special characters are for non-user interactions, like regular gun.get() path. for example '_' is path(gun.get())
Lorenzo Mangani @qxip
Great work tedd pasta
Wasis Haryo Sasoko
what does \u001b mean on the database file
Wasis Haryo Sasoko

@amark , I found something. gun.get("some").get("test") is not exactly the same as gun.get("some/test")

the first one, obey the reference / soul. the second one, it straight up doesn't give a shit about reference.
meaning using the example on the docs

gun.get('user/ambernadal/husband/name') // will result undefined 😭
gun.get('user').get('ambernadal').get('husband').get('name') // will resut Mark Nadal 💑
Wasis Haryo Sasoko
2 command, same path, different result

I haven't tested with null value, but my guess is, you can bypass the null value, by directly accessing the node using /

edit : just tested it. Yup, it straight up ignore the null value.

Wasis Haryo Sasoko
meaning, even tough you "think" you have deleted and nulled a node, and making its child orphan. the data still can be accessed using / instead of .get
Lorenzo Mangani @qxip
I believe that's by design in any graph right? with a null on a linked item you're just deleting a link and not the linked node, or its other node links
Barłomiej Bąk
@yokowasis there is yet another issue related to this. For Gun gun.get('user/amber/husband') and user gun.get('user').get('amber').get('husband') are completely different paths and if you create a SEA's certificate for the first one .put() in second approach will not work, certificate will be invalid
Wasis Haryo Sasoko
@lmangani:matrix.org I am talking about using / operator instead of get is a bad practice. Because they both behave differently. Using get operator gun behaves as expected traversing the tree. But using / it just doesn't follow the rules at all.
@dweorh_twitter nice to know. The docs should be edited. A lot of example using / operator.
11 replies
@lmangani:matrix.org using get, you can't traverse a disconnected / deleted link. But using / you can.
didn't mean to delete my message my bad
Using "/" in names, like "user/mnadal", "user/anadal" would be like having files called "user-mnadal" "user-anadal" in the same folder in a traditional filesystem
meaning: you never have a "user" folder, you just used that prefix to namespace files that way
so it makes sense that get("user").get("mnadal") is undefined, because you don't have a "user" folder, you just have a "user/mnadal" file in your root folder
I may be wrong (I'm still learning Gun as well) but that is how I understood it, and that would explain the behaviour that @yokowasis is facing
Barłomiej Bąk
hmm, we split the conversation into main and side thread now ;)
Wasis Haryo Sasoko
@simjnd:matrix.org in your case it probably doesn't have any difference. As long as no reference to other node or null (null is just a reference to the void), both of them will behave exactly the same.
It will become problematic when you have a complex tree, when a branch of the tree refer to another branch of tree. Perhaps you would be more familiar with the term join in mysql. It works something like that. But this will affect most people that use null to delete or in gun case, disconnect, the data and make the data orphan. In this case the / doesn't care about the disconnect branch. It will still see the branch is still there and connected, and you can't tell it otherwise.
Wasis Haryo Sasoko
@simjnd:matrix.org your understanding about the folder / file is actually incorrect.
when you do
    firstName : "Mark"

both of these command will result the same thing:


note that you can't do :

    firstName : "Mark"

it will throw error. because you need at least 2 get if you want to put something.

so, instead you can do :


and it exactly will result the same

Wasis Haryo Sasoko
ok, upon more inspection you are sort of right.
while reading doesn't really care about the tree structure. How you put data is very important.