├── LICENSE ├── README.md ├── main.go └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeromy Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipns-pub 2 | 3 | A tool to publish ipns entries on the ipfs network with a given key 4 | 5 | ## Usage 6 | First, you'll need to generate a publish key with [`ipfs-key`](https://github.com/whyrusleeping/ipfs-key). 7 | Once you have one, simply: 8 | ``` 9 | ipns-pub -key=mykeyfile /ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC 10 | ``` 11 | 12 | The program will start up an ipfs dht, bootstrap to the network, and publish the given 13 | ipfs path to the ipns entry of your given key. 14 | 15 | ## Installation 16 | ``` 17 | go get -d github.com/whyrusleeping/ipns-pub 18 | cd $GOPATH/src/github.com/whyrusleeping/ipns-pub 19 | gx --verbose install --global 20 | go install 21 | ``` 22 | 23 | Note: depends on gx, install with: 24 | ``` 25 | go get -u github.com/whyrusleeping/gx 26 | go get -u github.com/whyrusleeping/gx-go 27 | ``` 28 | 29 | ### License 30 | MIT 31 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "time" 9 | 10 | "gx/ipfs/QmPGpCi2W5SuzbYJE3NwuMtb95MPataUxuejYW1NLNr6sj/go-ipfs/namesys" 11 | path "gx/ipfs/QmPGpCi2W5SuzbYJE3NwuMtb95MPataUxuejYW1NLNr6sj/go-ipfs/path" 12 | "gx/ipfs/QmPGpCi2W5SuzbYJE3NwuMtb95MPataUxuejYW1NLNr6sj/go-ipfs/repo" 13 | "gx/ipfs/QmPGpCi2W5SuzbYJE3NwuMtb95MPataUxuejYW1NLNr6sj/go-ipfs/thirdparty/ipfsaddr" 14 | ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" 15 | cli "gx/ipfs/QmVcLF2CgjQb5BWmYFWsDfxDjbzBfcChfdHRedxeL3dV4K/cli" 16 | "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht" 17 | "gx/ipfs/QmWpTXhTkpoCDEm9twJd5Rc9jFwy61emzxneeJzrVMfjGF/go-libp2p-metrics" 18 | pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" 19 | ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" 20 | "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/host/basic" 21 | "gx/ipfs/QmeAfPWBWDQq9qjQ5oiWhaFs7oEsfB6FyEj5VxNdc2r34q/go-libp2p-swarm" 22 | "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" 23 | ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" 24 | ) 25 | 26 | const IpnsValidatorTag = "ipns" 27 | 28 | func fatal(i interface{}) { 29 | fmt.Fprintln(os.Stderr, i) 30 | os.Exit(1) 31 | } 32 | 33 | func main() { 34 | app := cli.NewApp() 35 | app.Name = "ipns-pub" 36 | app.Usage = `ipns-pub can be used to publish an ipns entry with a given private key 37 | 38 | Example: 39 | 40 | ipns-pub -key=mykeyfile /ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC 41 | 42 | Key Generation: 43 | 44 | To generate a key, install ipfs-key: 45 | 46 | go get github.com/whyrusleeping/ipfs-key 47 | 48 | And then run: 49 | 50 | ipfs-key > mykeyfile 51 | ` 52 | app.Flags = []cli.Flag{ 53 | cli.StringFlag{ 54 | Name: "key", 55 | Usage: "specify file containing key to publish with", 56 | }, 57 | cli.BoolFlag{ 58 | Name: "daemon", 59 | Usage: "run a daemon that republishes the given ipns entry", 60 | }, 61 | cli.StringFlag{ 62 | Name: "interval", 63 | Usage: "specify the republish interval for daemon mode (default 12h)", 64 | }, 65 | } 66 | app.Action = pubFunc 67 | 68 | err := app.Run(os.Args) 69 | if err != nil { 70 | fatal(err) 71 | } 72 | } 73 | 74 | func pubFunc(c *cli.Context) error { 75 | var priv ci.PrivKey 76 | if kf := c.String("key"); kf != "" { 77 | pk, err := loadKeyFile(kf) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | priv = pk 83 | } else { 84 | return fmt.Errorf("must specify key file with '--key'") 85 | } 86 | 87 | if !c.Args().Present() { 88 | return fmt.Errorf("must specify path to publish") 89 | } 90 | 91 | p, err := path.ParsePath(c.Args().First()) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | interv := c.String("interval") 97 | var ticktime time.Duration = time.Hour * 12 98 | if interv != "" { 99 | d, err := time.ParseDuration(interv) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | ticktime = d 105 | } 106 | 107 | bs, err := getBootstrapAddrs() 108 | if err != nil { 109 | return err 110 | } 111 | 112 | dstore := ds.NewMapDatastore() 113 | 114 | dht, err := spawnDHT(priv, dstore, bs) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | nsys := namesys.NewRoutingPublisher(dht, dstore) 120 | 121 | // publish once in either case 122 | err = nsys.Publish(context.TODO(), priv, p) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | if !c.Bool("daemon") { 128 | return nil 129 | } 130 | 131 | for range time.Tick(ticktime) { 132 | fmt.Println("publishing...") 133 | before := time.Now() 134 | err = nsys.Publish(context.TODO(), priv, p) 135 | if err != nil { 136 | // TODO: probably don't want to actually error out and die here 137 | return err 138 | } 139 | fmt.Println("publish took: ", time.Now().Sub(before)) 140 | } 141 | return nil 142 | } 143 | 144 | // currently just uses the ipfs core bootstrap nodes 145 | // TODO: allow user to specify their own 146 | func getBootstrapAddrs() ([]ma.Multiaddr, error) { 147 | addrs := []string{ 148 | "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", 149 | "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx", 150 | "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", 151 | "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", 152 | "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", 153 | "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", 154 | "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", 155 | "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", 156 | "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", 157 | } 158 | 159 | var maddrs []ma.Multiaddr 160 | for _, s := range addrs { 161 | a, err := ma.NewMultiaddr(s) 162 | if err != nil { 163 | return nil, err 164 | } 165 | 166 | maddrs = append(maddrs, a) 167 | } 168 | 169 | return maddrs, nil 170 | } 171 | 172 | func loadKeyFile(fi string) (ci.PrivKey, error) { 173 | data, err := ioutil.ReadFile(fi) 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | return ci.UnmarshalPrivateKey(data) 179 | } 180 | 181 | func spawnDHT(pk ci.PrivKey, dstore repo.Datastore, bootstraps []ma.Multiaddr) (*dht.IpfsDHT, error) { 182 | pub := pk.GetPublic() 183 | 184 | local, err := peer.IDFromPublicKey(pub) 185 | if err != nil { 186 | return nil, err 187 | } 188 | 189 | fmt.Println("Local peer ID: ", local.Pretty()) 190 | 191 | ps := pstore.NewPeerstore() 192 | ps.AddPrivKey(local, pk) 193 | ps.AddPubKey(local, pub) 194 | 195 | listenaddr, err := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0") 196 | if err != nil { 197 | return nil, err 198 | } 199 | 200 | s, err := swarm.NewNetwork(context.Background(), []ma.Multiaddr{listenaddr}, local, ps, metrics.NewBandwidthCounter()) 201 | if err != nil { 202 | fatal(err) 203 | } 204 | 205 | host := basichost.New(s) 206 | 207 | idht := dht.NewDHT(context.Background(), host, dstore) 208 | idht.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator 209 | idht.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc 210 | 211 | err = doBootstrap(host, idht, bootstraps) 212 | if err != nil { 213 | return nil, err 214 | } 215 | 216 | return idht, nil 217 | } 218 | 219 | func doBootstrap(bh *basichost.BasicHost, idht *dht.IpfsDHT, bootstraps []ma.Multiaddr) error { 220 | if len(bootstraps) > 0 { 221 | fmt.Println("Bootstrapping to:") 222 | for _, b := range bootstraps { 223 | fmt.Printf(" - %s\n", b) 224 | } 225 | fmt.Println() 226 | } 227 | 228 | errs := make(chan error) 229 | for _, bsaddr := range bootstraps { 230 | go func(bsa ma.Multiaddr) { 231 | iaddr, err := ipfsaddr.ParseMultiaddr(bsa) 232 | if err != nil { 233 | fmt.Println("error parsing bootstrap: ", err) 234 | errs <- err 235 | return 236 | } 237 | 238 | bh.Peerstore().AddAddr(iaddr.ID(), iaddr.Transport(), pstore.PermanentAddrTTL) 239 | 240 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 241 | defer cancel() 242 | 243 | pi := bh.Peerstore().PeerInfo(iaddr.ID()) 244 | err = bh.Connect(ctx, pi) 245 | if err != nil { 246 | fmt.Printf("error connecting to peer: %s\n", err) 247 | errs <- err 248 | return 249 | } 250 | 251 | fmt.Printf("dial to %s succeeded!\n", iaddr.ID()) 252 | 253 | idht.Update(context.TODO(), iaddr.ID()) 254 | errs <- nil 255 | }(bsaddr) 256 | } 257 | 258 | var good int 259 | for range bootstraps { 260 | err := <-errs 261 | if err == nil { 262 | good++ 263 | } 264 | } 265 | if good == 0 { 266 | return fmt.Errorf("couldnt connect to any bootstrap peers") 267 | } 268 | 269 | return nil 270 | } 271 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "whyrusleeping", 3 | "bugs": {}, 4 | "gx": { 5 | "dvcsimport": "github.com/whyrusleeping/ipns-pub" 6 | }, 7 | "gxDependencies": [ 8 | { 9 | "hash": "QmPGpCi2W5SuzbYJE3NwuMtb95MPataUxuejYW1NLNr6sj", 10 | "name": "go-ipfs", 11 | "version": "0.4.5-dev" 12 | } 13 | ], 14 | "gxVersion": "0.7.0", 15 | "issues_url": "", 16 | "language": "go", 17 | "license": "", 18 | "name": "ipns-pub", 19 | "version": "0.0.0" 20 | } 21 | 22 | --------------------------------------------------------------------------------