diff --git a/default-config.json b/default-config.json new file mode 100644 index 0000000000..27719324cd --- /dev/null +++ b/default-config.json @@ -0,0 +1,38 @@ +{ + "Addresses": { + "Swarm": [ + "/ip4/0.0.0.0/tcp/4001", + "/ip6/::/tcp/4001" + ], + "API": "/ip4/127.0.0.1/tcp/5001", + "Gateway": "/ip4/127.0.0.1/tcp/8080" + }, + "Discovery": { + "MDNS": { + "Enabled": true, + "Interval": 10 + } + }, + "Mounts": { + "IPFS": "/ipfs", + "IPNS": "/ipns" + }, + "Ipns": { + "ResolveCacheSize": 128 + }, + "Gateway": { + "RootRedirect": "", + "Writable": false + }, + "Bootstrap": [ + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", + "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", + "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", + "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", + "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", + "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx" + ] +} diff --git a/init-doc/about b/init-doc/about new file mode 100644 index 0000000000..eaa644b24b --- /dev/null +++ b/init-doc/about @@ -0,0 +1,53 @@ + + IPFS -- Inter-Planetary File system + +IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas +from Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single bit- +torrent swarm, exchanging git objects. IPFS provides an interface as simple +as the HTTP web, but with permanence built in. You can also mount the world +at /ipfs. + +IPFS is a protocol: +- defines a content-addressed file system +- coordinates content delivery +- combines Kademlia + BitTorrent + Git + +IPFS is a filesystem: +- has directories and files +- mountable filesystem (via FUSE) + +IPFS is a web: +- can be used to view documents like the web +- files accessible via HTTP at `http://ipfs.io/` +- browsers or extensions can learn to use `ipfs://` directly +- hash-addressed content guarantees authenticity + +IPFS is modular: +- connection layer over any network protocol +- routing layer +- uses a routing layer DHT (kademlia/coral) +- uses a path-based naming service +- uses bittorrent-inspired block exchange + +IPFS uses crypto: +- cryptographic-hash content addressing +- block-level deduplication +- file integrity + versioning +- filesystem-level encryption + signing support + +IPFS is p2p: +- worldwide peer-to-peer file transfers +- completely decentralized architecture +- **no** central point of failure + +IPFS is a cdn: +- add a file to the filesystem locally, and it's now available to the world +- caching-friendly (content-hash naming) +- bittorrent-based bandwidth distribution + +IPFS has a name service: +- IPNS, an SFS inspired name system +- global namespace based on PKI +- serves to build trust chains +- compatible with other NSes +- can map DNS, .onion, .bit, etc to IPNS diff --git a/init-doc/contact b/init-doc/contact new file mode 100644 index 0000000000..965de025e2 --- /dev/null +++ b/init-doc/contact @@ -0,0 +1,6 @@ +Come hang out in our IRC chat room if you have any questions. + +Contact the ipfs dev team: +- Bugs: https://github.com/ipfs/go-ipfs/issues +- Help: irc.freenode.org/#ipfs +- Email: dev@ipfs.io diff --git a/init-doc/docs/index b/init-doc/docs/index new file mode 100644 index 0000000000..c846c91b8c --- /dev/null +++ b/init-doc/docs/index @@ -0,0 +1 @@ +Index diff --git a/init-doc/help b/init-doc/help new file mode 100644 index 0000000000..d069d6ba76 --- /dev/null +++ b/init-doc/help @@ -0,0 +1,7 @@ +Some helpful resources for finding your way around ipfs: + +- quick-start: a quick show of various ipfs features. +- ipfs commands: a list of all commands +- ipfs --help: every command describes itself +- https://github.com/ipfs/go-ipfs -- the src repository +- #ipfs on irc.freenode.org -- the community irc channel diff --git a/init-doc/quick-start b/init-doc/quick-start new file mode 100644 index 0000000000..92eca892de --- /dev/null +++ b/init-doc/quick-start @@ -0,0 +1,113 @@ +# 0.1 - Quick Start + +This is a set of short examples with minimal explanation. It is meant as +a "quick start". Soon, we'll write a longer tour :-) + + +Add a file to ipfs: + + echo "hello world" >hello + ipfs add hello + + +View it: + + ipfs cat + + +Try a directory: + + mkdir foo + mkdir foo/bar + echo "baz" > foo/baz + echo "baz" > foo/bar/baz + ipfs add -r foo + + +View things: + + ipfs ls + ipfs ls /bar + ipfs cat /baz + ipfs cat /bar/baz + ipfs cat /bar + ipfs ls /baz + + +References: + + ipfs refs + ipfs refs -r + ipfs refs --help + + +Get: + + ipfs get -o foo2 + diff foo foo2 + + +Objects: + + ipfs object get + ipfs object get /foo2 + ipfs object --help + + +Pin + GC: + + ipfs pin add + ipfs repo gc + ipfs ls + ipfs pin rm + ipfs repo gc + + +Daemon: + + ipfs daemon (in another terminal) + ipfs id + + +Network: + + (must be online) + ipfs swarm peers + ipfs id + ipfs cat + + +Mount: + + (warning: fuse is finicky!) + ipfs mount + cd /ipfs/ + ls + + +Tool: + + ipfs version + ipfs update + ipfs commands + ipfs config --help + open http://localhost:5001/webui + + +Browse: + + webui: + + http://localhost:5001/webui + + video: + + http://localhost:8080/ipfs/QmVc6zuAneKJzicnJpfrqCH9gSy6bz54JhcypfJYhGUFQu/play#/ipfs/QmTKZgRNwDNZwHtJSjCp6r5FYefzpULfy37JvMt9DwvXse + + images: + + http://localhost:8080/ipfs/QmZpc3HvfjEXvLWGQPWbHk3AjD5j8NEN4gmFN8Jmrd5g83/cs + + markdown renderer app: + + http://localhost:8080/ipfs/QmX7M9CiYXjVeFnkfVGf3y5ixTZ2ACeSGyL1vBJY1HvQPp/mdown diff --git a/init-doc/readme b/init-doc/readme new file mode 100644 index 0000000000..eeade6617b --- /dev/null +++ b/init-doc/readme @@ -0,0 +1,26 @@ +Hello and Welcome to IPFS! + +██╗██████╗ ███████╗███████╗ +██║██╔══██╗██╔════╝██╔════╝ +██║██████╔╝█████╗ ███████╗ +██║██╔═══╝ ██╔══╝ ╚════██║ +██║██║ ██║ ███████║ +╚═╝╚═╝ ╚═╝ ╚══════╝ + +If you're seeing this, you have successfully installed +IPFS and are now interfacing with the ipfs merkledag! + + ------------------------------------------------------- +| Warning: | +| This is alpha software. Use at your own discretion! | +| Much is missing or lacking polish. There are bugs. | +| Not yet secure. Read the security notes for more. | + ------------------------------------------------------- + +Check out some of the other files in this directory: + + ./about + ./help + ./quick-start <-- usage examples + ./readme <-- this file + ./security-notes diff --git a/init-doc/security-notes b/init-doc/security-notes new file mode 100644 index 0000000000..da55ea71c4 --- /dev/null +++ b/init-doc/security-notes @@ -0,0 +1,21 @@ + IPFS Alpha Security Notes + +We try hard to ensure our system is safe and robust, but all software +has bugs, especially new software. This distribution is meant to be an +alpha preview, don't use it for anything mission critical. + +Please note the following: + +- This is alpha software and has not been audited. It is our goal + to conduct a proper security audit once we close in on a 1.0 release. + +- ipfs is a networked program, and may have serious undiscovered + vulnerabilities. It is written in Go, and we do not execute any + user provided data. But please point any problems out to us in a + github issue, or email security@ipfs.io privately. + +- ipfs uses encryption for all communication, but it's NOT PROVEN SECURE + YET! It may be totally broken. For now, the code is included to make + sure we benchmark our operations with encryption in mind. In the future, + there will be an "unsafe" mode for high performance intranet apps. + If this is a blocking feature for you, please contact us. diff --git a/init-doc/tour/0.0-intro b/init-doc/tour/0.0-intro new file mode 100644 index 0000000000..36d8da0c6d --- /dev/null +++ b/init-doc/tour/0.0-intro @@ -0,0 +1,34 @@ +WIP + +# 0.0 - Introduction + +Welcome to IPFS! This tour will guide you through a few of the +features of this tool, and the most common commands. Then, it will +immerse you into the world of merkledags and the amazing things +you can do with them. + + +This tour has many parts, and can be taken in different sequences. +Different people learn different ways, so choose your own adventure: + + To start with the concepts, try: + - The Merkle DAG + - Data Structures on the Merkle DAG + - Representing Files with unixfs + - add, cat, ls, refs + ... + + + To start with the examples, try: + - add, cat, ls, refs + - Representing Files with unixfs + - Data Structures on the Merkle DAG + - The Merkle DAG + ... + + + To start with the network, try: + - IPFS Nodes + - Running the daemon + - The Swarm + - The Web diff --git a/package.json b/package.json index cc9fc610a1..16ab38a9af 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "hapi": "^12.0.0", "ipfs-api": "^2.13.1", "ipfs-blocks": "^0.1.0", - "ipfs-data-importing": "^0.3.0", + "ipfs-data-importing": "^0.3.3", "ipfs-merkle-dag": "^0.2.1", "ipfs-multipart": "^0.1.0", "ipfs-repo": "^0.5.0", @@ -90,7 +90,7 @@ "libp2p-ipfs": "^0.2.0", "lodash.get": "^4.0.0", "lodash.set": "^4.0.0", - "peer-id": "^0.6.0", + "peer-id": "^0.6.1", "peer-info": "^0.6.0", "ronin": "^0.3.11", "temp": "^0.8.3" diff --git a/src/core/index.js b/src/core/index.js index 2e66dea3a2..682ca3ae05 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -1,18 +1,18 @@ 'use strict' const defaultRepo = require('./default-repo') -// const bl = require('bl') const blocks = require('ipfs-blocks') const BlockService = blocks.BlockService const Block = blocks.Block const mDAG = require('ipfs-merkle-dag') const DAGNode = mDAG.DAGNode const DAGService = mDAG.DAGService -const Id = require('peer-id') -const Info = require('peer-info') +const peerId = require('peer-id') +const PeerInfo = require('peer-info') const multiaddr = require('multiaddr') const importer = require('ipfs-data-importing').import const libp2p = require('libp2p-ipfs') +const init = require('./init') exports = module.exports = IPFS @@ -40,8 +40,8 @@ function IPFS (repo) { if (err) { throw err } - const pid = Id.createFromPrivKey(config.Identity.PrivKey) - peerInfo = new Info(pid) + const pid = peerId.createFromPrivKey(config.Identity.PrivKey) + peerInfo = new PeerInfo(pid) config.Addresses.Swarm.forEach((addr) => { peerInfo.multiaddr.add(multiaddr(addr)) }) @@ -107,6 +107,8 @@ function IPFS (repo) { gc: function () {} } + this.init = (opts, callback) => { init(repo, opts, callback) } + this.bootstrap = { list: (callback) => { repo.config.get((err, config) => { diff --git a/src/core/init.js b/src/core/init.js new file mode 100644 index 0000000000..a491f0c40a --- /dev/null +++ b/src/core/init.js @@ -0,0 +1,81 @@ +const peerId = require('peer-id') +const IpfsBlocks = require('ipfs-blocks').BlockService +const IpfsDagService = require('ipfs-merkle-dag').DAGService +const path = require('path') + +module.exports = (repo, opts, callback) => { + opts = opts || {} + opts.force = opts.force || false + opts.emptyRepo = opts.emptyRepo || false + opts.bits = opts.bits || 2048 + + // Pre-set config values. + var config = require('../../default-config.json') + + // Verify repo does not yet exist (or that 'force' is provided). + repo.exists((err, res) => { + if (err) { return callback(err) } + if (res === true && !opts.force) { + return callback(new Error('repo already exists and \'force\' is not set')) + } + + generateAndSetKeypair() + }) + + // Generate peer identity keypair + transform to desired format + add to config. + function generateAndSetKeypair () { + var keys = peerId.create({ + bits: opts.bits + }) + config.Identity = { + PeerID: keys.toB58String(), + PrivKey: keys.privKey.toString('base64') + } + + writeVersion() + } + + function writeVersion () { + const version = '3' + repo.version.set(version, (err) => { + if (err) { return callback(err) } + + writeConfig() + }) + } + + // Write the config to the repo. + function writeConfig () { + repo.config.set(config, (err) => { + if (err) { return callback(err) } + + addDefaultAssets() + }) + } + + // Add the default assets to the repo. + function addDefaultAssets () { + // Skip this step on the browser, or if emptyRepo was supplied. + const isNode = !global.window + if (!isNode || opts.emptyRepo) { + return doneImport(null) + } + + const importer = require('ipfs-data-importing') + const blocks = new IpfsBlocks(repo) + const dag = new IpfsDagService(blocks) + + const initDocsPath = path.join(__dirname, '../../init-doc') + + importer.import(initDocsPath, dag, { + recursive: true + }, doneImport) + + function doneImport (err, stat) { + if (err) { return callback(err) } + + // All finished! + callback(null, true) + } + } +} diff --git a/tests/test-core/browser.js b/tests/test-core/browser.js index 9a6e631e32..0ae765a540 100644 --- a/tests/test-core/browser.js +++ b/tests/test-core/browser.js @@ -48,12 +48,7 @@ describe('IPFS Repo Tests on the Browser', function () { testsContext .keys() .filter((key) => { - if (key === './test-swarm-node.js' || - key === './test-swarm-node') { - return false - } else { - return true - } + return !(key.endsWith('-node.js') || key.endsWith('-node')) }) .forEach((key) => { testsContext(key) diff --git a/tests/test-core/index.js b/tests/test-core/index.js index e1d9e20eb8..ba0c3159ca 100644 --- a/tests/test-core/index.js +++ b/tests/test-core/index.js @@ -27,8 +27,7 @@ describe('core', () => { const tests = fs.readdirSync(__dirname) tests.filter((file) => { if (file === 'index.js' || - file === 'browser.js' || - file === 'test-swarm-browser.js') { + file.endsWith('browser.js')) { return false } else { return true diff --git a/tests/test-core/test-init-node.js b/tests/test-core/test-init-node.js new file mode 100644 index 0000000000..3144d6c74d --- /dev/null +++ b/tests/test-core/test-init-node.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ + +const expect = require('chai').expect +const IPFS = require('../../src/core') +const IPFSRepo = require('ipfs-repo') + +function createTestRepo () { + const repoPath = '/tmp/ipfs-test-' + Math.random().toString().substring(2, 8) + '/' + + var store + var teardown + + const isNode = !global.window + if (isNode) { + store = require('fs-blob-store') + teardown = (done) => { + const rimraf = require('rimraf') + rimraf(repoPath, (err) => { + expect(err).to.not.exist + done() + }) + } + } else { + const idb = window.indexedDB || + window.mozIndexedDB || + window.webkitIndexedDB || + window.msIndexedDB + store = require('idb-plus-blob-store') + teardown = (done) => { + idb.deleteDatabase(repoPath) + idb.deleteDatabase(repoPath + '/blocks') + done() + } + } + + const options = { + bits: 64, + stores: { + keys: store, + config: store, + datastore: store, + logs: store, + locks: store, + version: store + } + } + + var repo = new IPFSRepo(repoPath, options) + + repo.teardown = teardown + + return repo +} + +describe('node: init', function () { + this.timeout(10000) + + it('init docs written', (done) => { + var repo = createTestRepo() + const ipfs = new IPFS(repo) + ipfs.init({ bits: 64 }, (err) => { + expect(err).to.not.exist + + // Check for default assets + var multihash = new Buffer('12205e7c3ce237f936c76faf625e90f7751a9f5eeb048f59873303c215e9cce87599', 'hex') + ipfs.object.get(multihash, {}, (err, node) => { + expect(err).to.not.exist + expect(node.links).to.exist + + repo.teardown(done) + }) + }) + }) + + it('empty repo', (done) => { + var repo = createTestRepo() + const ipfs = new IPFS(repo) + ipfs.init({ bits: 64, emptyRepo: true }, (err) => { + expect(err).to.not.exist + + // Check for default assets + var multihash = new Buffer('12205e7c3ce237f936c76faf625e90f7751a9f5eeb048f59873303c215e9cce87599', 'hex') + ipfs.object.get(multihash, {}, (err, node) => { + expect(err).to.exist + repo.teardown(done) + }) + }) + }) +}) diff --git a/tests/test-core/test-init.js b/tests/test-core/test-init.js new file mode 100644 index 0000000000..6cdbf0deba --- /dev/null +++ b/tests/test-core/test-init.js @@ -0,0 +1,123 @@ +/* eslint-env mocha */ + +const expect = require('chai').expect +const IPFS = require('../../src/core') +const IPFSRepo = require('ipfs-repo') + +function createTestRepo () { + const repoPath = '/tmp/ipfs-test-' + Math.random().toString().substring(2, 8) + '/' + + var store + var teardown + + const isNode = !global.window + if (isNode) { + store = require('fs-blob-store') + teardown = (done) => { + const rimraf = require('rimraf') + rimraf(repoPath, (err) => { + expect(err).to.not.exist + done() + }) + } + } else { + const idb = window.indexedDB || + window.mozIndexedDB || + window.webkitIndexedDB || + window.msIndexedDB + store = require('idb-plus-blob-store') + teardown = (done) => { + idb.deleteDatabase(repoPath) + idb.deleteDatabase(repoPath + '/blocks') + done() + } + } + + const options = { + bits: 64, + stores: { + keys: store, + config: store, + datastore: store, + logs: store, + locks: store, + version: store + } + } + + var repo = new IPFSRepo(repoPath, options) + + repo.teardown = teardown + + return repo +} + +describe('init', function () { + this.timeout(10000) + + it('basic', (done) => { + var repo = createTestRepo() + const ipfs = new IPFS(repo) + ipfs.init({ emptyRepo: true }, (err) => { + expect(err).to.not.exist + + repo.exists((err, res) => { + expect(err).to.not.exist + expect(res).to.equal(true) + + repo.config.get((err, config) => { + expect(err).to.not.exist + expect(config.Identity).to.exist + + repo.teardown(done) + }) + }) + }) + }) + + it('set # of bits in key', (done) => { + var repo1 = createTestRepo() + var repo2 = createTestRepo() + const ipfsShort = new IPFS(repo1) + const ipfsLong = new IPFS(repo2) + ipfsShort.init({ bits: 128, emptyRepo: true }, (err) => { + expect(err).to.not.exist + + ipfsLong.init({ bits: 256, emptyRepo: true }, (err) => { + expect(err).to.not.exist + + repo1.config.get((err, config1) => { + expect(err).to.not.exist + + repo2.config.get((err, config2) => { + expect(err).to.not.exist + expect(config1.Identity.PrivKey.length).is.below(config2.Identity.PrivKey.length) + + repo1.teardown(() => { + repo2.teardown(done) + }) + }) + }) + }) + }) + }) + + it('force init (overwrite)', (done) => { + var repo = createTestRepo() + const ipfs1 = new IPFS(repo) + const ipfs2 = new IPFS(repo) + ipfs1.init({ bits: 128, emptyRepo: true }, (err) => { + expect(err).to.not.exist + + ipfs2.init({ bits: 128, force: false }, (err) => { + expect(err).to.exist + + ipfs2.init({ force: true }, (err) => { + expect(err).to.not.exist + + repo.teardown(done) + }) + }) + }) + }) +})