Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add MFS tutorial #200

Merged
merged 84 commits into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from 80 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
1493427
Surface MFS tutorials
terichadbourne Apr 22, 2019
21e0e81
Grammar fixes
terichadbourne Apr 22, 2019
0316e88
update lesson validation wording
terichadbourne Apr 22, 2019
c80c954
change _solution to solution
terichadbourne Apr 23, 2019
bd96f59
Merge branch 'code' into feat/mfs-tutorial
terichadbourne Apr 23, 2019
db8d2c7
add missing solution code to mfs lesson 2
terichadbourne Apr 23, 2019
ff96c55
add solution prop to MFS lessons and boilerplates
terichadbourne Apr 23, 2019
a126b30
Merge branch 'code' into feat/mfs-tutorial
terichadbourne Apr 26, 2019
c500fd4
Merge branch 'code' into feat/mfs-tutorial
fsdiogo Apr 26, 2019
8b00bb8
Rework lesson order in MFS tutorial
terichadbourne Apr 26, 2019
196593e
Merge branch 'code' into feat/mfs-tutorial
terichadbourne Apr 29, 2019
ecea23b
Update stat lesson content
terichadbourne Apr 29, 2019
fa33bee
Fix syntax error and validation in stat lesson
terichadbourne Apr 29, 2019
78d9639
update lesson on stating again
terichadbourne Apr 29, 2019
6e201df
swap order of lessons 2 and 3
terichadbourne Apr 29, 2019
4c356ca
Rework messaging for IPFS introk
terichadbourne Apr 29, 2019
09e2af0
Merge branch 'code' into feat/mfs-tutorial
terichadbourne Apr 30, 2019
b786fbb
Apply new logging feature to file upload demo
terichadbourne Apr 30, 2019
39e139a
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 2, 2019
888dfee
Validate directory creation for mkdir (MFS #7)
terichadbourne May 2, 2019
45cec48
chore: simplify js object files logging
fsdiogo May 3, 2019
424c0a6
add validation for missing {parents:true}
terichadbourne May 3, 2019
8f7872c
add validation for use of ls in stat lesson
terichadbourne May 3, 2019
6e4cae3
update text of lessons 3, 4, 5
terichadbourne May 3, 2019
9f9281f
improve formatting of references to root directory
terichadbourne May 3, 2019
d293b45
improve example for mkdir lesson
terichadbourne May 3, 2019
0906027
add validation for other errors in mkdir lesson 7
terichadbourne May 3, 2019
2ed5e24
add blank space around user code area
terichadbourne May 3, 2019
e401847
add validation for matching filenames in #7
terichadbourne May 6, 2019
6655da7
remove console logs
terichadbourne May 6, 2019
ced1bb8
validate ls-ing wrong directory in #7
terichadbourne May 6, 2019
e513197
improve error msg when returning wrong ls in #7
terichadbourne May 6, 2019
2a4a272
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 7, 2019
d78057b
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 7, 2019
66592b6
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 7, 2019
e5d8d94
add boilerplates for lessons 8-12
terichadbourne May 7, 2019
1473267
correct relative filepaths in readme
terichadbourne May 7, 2019
66c06e5
fix typo lesson 7
terichadbourne May 7, 2019
5347613
add lesson 8 text
terichadbourne May 7, 2019
d1abb57
fix typo and scope issues lsn 7
terichadbourne May 7, 2019
13cdf99
update lesson 8 instructions
terichadbourne May 7, 2019
158cbc4
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 8, 2019
6aff4e1
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 8, 2019
d78163f
add globals to boilerplates
terichadbourne May 8, 2019
48e6a9b
add code and first pass validation for lsn 8
terichadbourne May 8, 2019
45ac12e
attempt to test for incorrect use of await
terichadbourne May 8, 2019
e03f04a
test for mistakenly moving directory
terichadbourne May 8, 2019
e81939c
swap order of validation to fix bug
terichadbourne May 8, 2019
312c243
add first draft of files.cp lesson
terichadbourne May 8, 2019
e306c48
tweak lesson 9 solution display
terichadbourne May 8, 2019
3314cbf
first draft of files.read lesson
terichadbourne May 8, 2019
812e4e7
lesson 10 validation edits
terichadbourne May 9, 2019
f751225
fix: lesson 7 validations
fsdiogo May 9, 2019
5e26cea
address await problems in lesson 8
terichadbourne May 9, 2019
e9ec6ff
add text for lesson 11
terichadbourne May 9, 2019
05e5bc7
revamp lesson 8 mv solution
terichadbourne May 10, 2019
7f49d1e
remove lesson 12
terichadbourne May 10, 2019
4cb1298
add initial validation for lesson 11 rm
terichadbourne May 10, 2019
ce71d33
attempt to add test file to non-mfs ipfs
terichadbourne May 10, 2019
4b84b75
Merge branch 'code' into feat/mfs-tutorial
terichadbourne May 13, 2019
13ad8f5
add notes on TBValidated lessons
terichadbourne May 13, 2019
bc3ff5e
chore: lessons cleanup
fsdiogo May 20, 2019
3fc3726
chore: update lesson 09
fsdiogo May 21, 2019
e342f59
feat: lesson 9 now copies file from ipfs
fsdiogo May 22, 2019
b547b15
feat: make lesson 10 work
fsdiogo May 22, 2019
8dce93f
feat: finish lesson 11
fsdiogo May 23, 2019
6b17177
fix: appease linter
fsdiogo May 23, 2019
32181d0
fix: LessonLink spacing
fsdiogo May 23, 2019
c33cfc1
Merge branch 'code' into feat/mfs-tutorial
fsdiogo May 23, 2019
6b9b527
fix: grow file upload container
fsdiogo Jun 3, 2019
b517b9d
chore: apply suggestions from code review
fsdiogo Jun 3, 2019
3b02330
chore: replace folder word with directory
fsdiogo Jun 3, 2019
b7fa36d
chore: rephrase mfs editing
fsdiogo Jun 3, 2019
57881c7
chore: typos
fsdiogo Jun 3, 2019
a9c0350
text edits
terichadbourne Jun 5, 2019
97a94e8
make user name file when copying
terichadbourne Jun 5, 2019
5b17226
improve lesson 9 validation
terichadbourne Jun 5, 2019
7ee253b
read from success.txt in lesson 10
terichadbourne Jun 5, 2019
eef27cd
improve validation for lesson 11
terichadbourne Jun 5, 2019
53bdd02
update headline and lesson titles
terichadbourne Jun 5, 2019
8a6c254
chore: update mfs description
fsdiogo Jun 6, 2019
b9ffb61
fix: add await to ipfs.files.mv
fsdiogo Jun 6, 2019
1c0c52b
chore: tidying up
fsdiogo Jun 6, 2019
f5d62e8
feat: add loading animation
fsdiogo Jun 6, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Select the appropriate boilerplate Vue file for your lesson from the `tutorials/
- `boilerplate-file-upload.vue` for a lesson with a coding exercise that requires a file upload
- `boilerplate-no-exercise.vue` for a text-only lesson

Copy that boilerplate into your tutorial folder and rename it to the 2-digit number of the lesson.
Copy that boilerplate into your tutorial directory and rename it to the 2-digit number of the lesson.

Example (while in `src/tutorials`):

Expand Down Expand Up @@ -254,7 +254,7 @@ you need to override, as in this example:
```js
} else if (result.error && result.error.message === 'No child name passed to addLink') {
// Forgot the file name and just used a directory as the path
return { fail: 'Uh oh. It looks like you created a folder instead of a file. Did you forget to include a filename in your path?' }
return { fail: 'Uh oh. It looks like you created a directory instead of a file. Did you forget to include a filename in your path?' }
}
```
Be sure to adapt your test case so that it works within the context of your other conditionals to meet your validation needs. What is required is that you return an object with the `fail` key and a string as its value; that string is what will be shown to the user.
Expand Down
2 changes: 1 addition & 1 deletion src/components/File-Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default {
for (let f of Array.from(event.dataTransfer.items)) {
let isFile = f.getAsEntry ? f.getAsEntry().isFile : (f.webkitGetAsEntry ? f.webkitGetAsEntry().isFile : true)
if (!isFile) {
return alert("Folder upload is not supported. Please select a file or multiple files.")
return alert("Directory upload is not supported. Please select one or more files.")
}
}
this.onFiles(files)
Expand Down
20 changes: 16 additions & 4 deletions src/components/Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@
<label for="add-files" class="flex items-center h4 pointer">
<svg viewBox="0 0 100 100" class="fill-aqua" height="60px" alt="Add"><path d="M71.13 28.87a29.88 29.88 0 1 0 0 42.26 29.86 29.86 0 0 0 0-42.26zm-18.39 37.6h-5.48V52.74H33.53v-5.48h13.73V33.53h5.48v13.73h13.73v5.48H52.74z"></path></svg>
<div class="f5 charcoal">
<p><strong>Drop one or more files here or click to select.</strong> Folder upload is not supported, but you may select multiple files using Ctrl+Click or Command+Click.</p>
<p><strong>Drop one or more files here or click to select.</strong> Directory upload is not supported, but you may select multiple files using Ctrl+Click or Command+Click.</p>
</div>
</label>
</div>
</div>
<div v-else class="mt2">
<span v-on:click="resetFileUpload" class="textLink fr pb1">Start Over</span>
<div class="mb2 pl3 pa2 w-100 br3 h4 shadow-4 bg-white color-navy flex items-center">
<div class="mb2 pl3 pa2 w-100 br3 shadow-4 bg-white color-navy flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="fill-aqua" height="60px"><path d="M55.94 19.17H30a4 4 0 0 0-4 4v53.65a4 4 0 0 0 4 4h40.1a4 4 0 0 0 4-4V38.06zm5.28 21.08c-4.33 0-7.47-2.85-7.47-6.77V21l18.13 19.25z"/></svg>
<ul class="list pl0">
<li v-for="(file, idx) in uploadedFiles" :key="`file-${idx}`">{{file.name}}</li>
Expand Down Expand Up @@ -265,6 +265,7 @@ export default {
lessonKey: 'passed' + self.$route.path,
lessonPassed: !!localStorage['passed' + self.$route.path],
lessonTitle: self.$attrs.lessonTitle,
createTestFile: self.$attrs.createTestFile,
output: self.output,
expandExercise: false,
dragging: false,
Expand Down Expand Up @@ -343,8 +344,10 @@ export default {
}
let output = this.output
let ipfs = await this.createIPFS()
if (this.createTestFile) {
await this.createFile(ipfs)
}
let code = this.editor.getValue()

let modules = {}
if (this.$attrs.modules) modules = this.$attrs.modules
if (this.isFileLesson) args.unshift(this.uploadedFiles)
Expand Down Expand Up @@ -374,11 +377,20 @@ export default {
return this.$attrs.createIPFS()
} else {
let ipfs = this.IPFSPromise.then(IPFS => {
return new IPFS({repo: Math.random().toString()})
this.ipfsConstructor = IPFS
return new IPFS({ repo: Math.random().toString() })
})
return ipfs
}
},
createFile: function (ipfs) {
new Promise((resolve, reject) => {
ipfs.on('ready', async () => {
await ipfs.add(this.ipfsConstructor.Buffer.from('You did it!'))
resolve()
})
})
},
resetCode: function () {
// TRACK? User chose to reset code
this.code = this.$attrs.code || defaultCode
Expand Down
4 changes: 2 additions & 2 deletions src/components/LessonLink.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<router-link :to="to" class="link db pa3 bb b--white green hover-bg-washed-yellow">
<div class="flex">
<div class="green ttu f6" style="min-width: 72px">Lesson {{index}}</div>
<div class="pr2">
<div class="tc green ttu f6" style="min-width: 92px">Lesson {{index}}</div>
<div class="pr3">
<img v-if="lessonPassed('passed' + to)" src="../static/images/complete.svg" alt="complete" style="height: 1rem;"/>
<img v-else-if="lessonCached('cached' + to)" src="../static/images/in-progress.svg" alt="in progress" style="height: 1rem;"/>
<img v-else src="../static/images/not-started.svg" alt="not yet started" style="height: 0.9rem;"/>
Expand Down
34 changes: 23 additions & 11 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@ import LessonDataStructures02 from './tutorials/Data-Structures/02.vue'
import LessonDataStructures03 from './tutorials/Data-Structures/03.vue'
import LessonDataStructures04 from './tutorials/Data-Structures/04.vue'
import LessonDataStructures05 from './tutorials/Data-Structures/05.vue'
// import MutableFileSystem01 from './tutorials/Mutable-File-System/01.vue'
// import MutableFileSystem02 from './tutorials/Mutable-File-System/02.vue'
// import MutableFileSystem03 from './tutorials/Mutable-File-System/03.vue'
// import MutableFileSystem04 from './tutorials/Mutable-File-System/04.vue'
// import MutableFileSystem05 from './tutorials/Mutable-File-System/05.vue'
import MutableFileSystem01 from './tutorials/Mutable-File-System/01.vue'
import MutableFileSystem02 from './tutorials/Mutable-File-System/02.vue'
import MutableFileSystem03 from './tutorials/Mutable-File-System/03.vue'
import MutableFileSystem04 from './tutorials/Mutable-File-System/04.vue'
import MutableFileSystem05 from './tutorials/Mutable-File-System/05.vue'
import MutableFileSystem06 from './tutorials/Mutable-File-System/06.vue'
import MutableFileSystem07 from './tutorials/Mutable-File-System/07.vue'
import MutableFileSystem08 from './tutorials/Mutable-File-System/08.vue'
import MutableFileSystem09 from './tutorials/Mutable-File-System/09.vue'
import MutableFileSystem10 from './tutorials/Mutable-File-System/10.vue'
import MutableFileSystem11 from './tutorials/Mutable-File-System/11.vue'

Vue
.use(VueRouter)
Expand Down Expand Up @@ -70,12 +76,18 @@ const routes = [
{ path: '/blog/06', component: LessonBlog06 },
{ path: '/blog/07', component: LessonBlog07 },
// Lessons - MFS
// { path: '/mutable-file-system', component: Landing, props: { tutorialId: 'mutableFileSystem' } },
// { path: '/mutable-file-system/01', component: MutableFileSystem01 },
// { path: '/mutable-file-system/02', component: MutableFileSystem02 },
// { path: '/mutable-file-system/03', component: MutableFileSystem03 },
// { path: '/mutable-file-system/04', component: MutableFileSystem04 },
// { path: '/mutable-file-system/05', component: MutableFileSystem05 },
{ path: '/mutable-file-system', component: Landing, props: { tutorialId: 'mutableFileSystem' } },
{ path: '/mutable-file-system/01', component: MutableFileSystem01 },
{ path: '/mutable-file-system/02', component: MutableFileSystem02 },
{ path: '/mutable-file-system/03', component: MutableFileSystem03 },
{ path: '/mutable-file-system/04', component: MutableFileSystem04 },
{ path: '/mutable-file-system/05', component: MutableFileSystem05 },
{ path: '/mutable-file-system/06', component: MutableFileSystem06 },
{ path: '/mutable-file-system/07', component: MutableFileSystem07 },
{ path: '/mutable-file-system/08', component: MutableFileSystem08 },
{ path: '/mutable-file-system/09', component: MutableFileSystem09 },
{ path: '/mutable-file-system/10', component: MutableFileSystem10 },
{ path: '/mutable-file-system/11', component: MutableFileSystem11 },
// 404
{ path: '*', name: '404' }
]
Expand Down
4 changes: 2 additions & 2 deletions src/static/courses.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"all": ["dataStructures", "basics", "blog"],
"featured": ["dataStructures", "basics", "blog"]
"all": ["mutableFileSystem", "dataStructures", "basics", "blog"],
"featured": ["mutableFileSystem", "dataStructures", "basics", "blog"]
}
20 changes: 13 additions & 7 deletions src/static/tutorials.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,20 @@
},
"mutableFileSystem": {
"project": "IPFS",
"title": "IPFS as a Mutable File System",
"description": "Store, edit, and share files with with the Mutable File System (MFS).",
"title": "IPFS: Mutable File System",
"description": "All data in IPFS is content-addressed and immutable, but the Mutable File System (MFS) lets you work with files and directories as though you were using a traditional name-based file system.",
fsdiogo marked this conversation as resolved.
Show resolved Hide resolved
"lessons": [
{ "to": "/mutable-file-system/01", "name": "Working with your IPFS node" },
{ "to": "/mutable-file-system/02", "name": "Working with files in ProtoSchool" },
{ "to": "/mutable-file-system/03", "name": "Add a new file to MFS" },
{ "to": "/mutable-file-system/04", "name": "View files in your directory" },
{ "to": "/mutable-file-system/05", "name": "Create a directory" }
{ "to": "/mutable-file-system/01", "name": "Introducing IPFS" },
{ "to": "/mutable-file-system/02", "name": "Check the status of a directory" },
{ "to": "/mutable-file-system/03", "name": "Working with files in ProtoSchool" },
{ "to": "/mutable-file-system/04", "name": "Add a file to MFS" },
{ "to": "/mutable-file-system/05", "name": "View the contents of a directory" },
{ "to": "/mutable-file-system/06", "name": "See how CIDs change as data changes" },
{ "to": "/mutable-file-system/07", "name": "Create a directory" },
{ "to": "/mutable-file-system/08", "name": "Move a file or directory" },
{ "to": "/mutable-file-system/09", "name": "Copy a file or directory" },
{ "to": "/mutable-file-system/10", "name": "Read the contents of a file" },
{ "to": "/mutable-file-system/11", "name": "Remove a file or directory" }
]
}
}
4 changes: 2 additions & 2 deletions src/tutorials/Data-Structures/04.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ Every `CID` is an identifier that contains the `codec` to interpret the data and

`CID`s allow us to build data structures that link to other data structures
in completely different formats. Imagine a tree of JSON objects that link
to BSON objects that also link to git commits. (Or imagine a file folder containing
puppy images <em>and</em> kitty videos, with a subfolder containing articles on
to BSON objects that also link to git commits. (Or imagine a directory containing
puppy images <em>and</em> kitty videos, with a subdirectory containing articles on
giraffes. The possiblities are endless!) All the way down this tree we
have cryptographic hashes that allow us to distribute and link the data.

Expand Down
4 changes: 2 additions & 2 deletions src/tutorials/Data-Structures/05.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ that we can think of as a link, so a Merkle tree is a collection of linked nodes

As previously discussed, all content addresses are unique to the data they represent. In the graph above, `node E` contains a reference to the hash for `node F` and `node G`. This means that the content address (hash) of `node E` is unique to a node containing those addresses.

Getting lost? Let's imagine this as a set of file folders. If we run folder E through our hashing algorithm
while it contains subfolders F and G, the content-derived hash we get back will include references to those two folders. If we remove folder G, it's like Grace removing that whisker from her kitten photo. Folder E doesn't have the same contents anymore, so it gets a new hash.
Getting lost? Let's imagine this as a set of directories, or file folders. If we run directory E through our hashing algorithm
while it contains subdirectories F and G, the content-derived hash we get back will include references to those two directories. If we remove directory G, it's like Grace removing that whisker from her kitten photo. Directory E doesn't have the same contents anymore, so it gets a new hash.

As the tree above is built, the final content address (hash) of the root node is unique to a
tree that contains every node all the way down this tree. If the data in any node were
Expand Down
21 changes: 13 additions & 8 deletions src/tutorials/Mutable-File-System/01.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@

## Data added to IPFS is stored in a local node
## IPFS: The InterPlanetary File System

One of the most common questions for those new to IPFS is where content lives when it's added to the network.
[IPFS](https://ipfs.io/), or the InterPlanetary File System, is a peer-to-peer (P2P) networking protocol used to share data on the distributed web. As its full name suggests, you can think of IPFS as a file system, and it has some unique characteristics that make it ideal for safe, decentralized sharing.

As a peer-to-peer data storage system, IPFS allows each user (peer) to host whatever data they'd like locally. Typically you'd install IPFS on your own computer and create a new instance of IPFS (also known as a node) there. That's where your data would live locally, referenced by content addresses (CIDs). Data stored in IPFS can take many forms, but one of the most common use cases is the sharing of tradional files, which we'll learn more about in this tutorial.
If you haven't yet done so, we encourage you to check out our [Decentralized Data Structures tutorial](https://proto.school/#/data-structures/), where you can learn all about the decentralized web and how it compares to the web you're accustomed to. There you'll learn all about content addressing, cryptographic hashing, Content Identifiers (CIDs), and sharing with peers, all of which you'll need to understand to make the most of this tutorial on IPFS.

You could choose to share your data or files with your peers when you had a network connection, but if you were the only one hosting a particular resource, it would become unavailable to your peers when your machine went offline. Having multiple peers hosting the same files is what makes them more readily available, and using CIDs is what makes this system secure. We'll talk more about sharing in future tutorials, but for now we'll focus on how to work with files within your own IPFS instance.
## Storing and sharing data in IPFS

## Working with your IPFS node in ProtoSchool
Where does content live when it's added to the IPFS network?

Here in our ProtoSchool tutorials, rather than using your machine itself, we're creating a new IPFS node for you in the browser each time you hit the "Submit" button in a lesson.
As a peer-to-peer data storage system, IPFS allows each user (peer) to host whatever data they'd like locally. When you first add new content to IPFS, you're really just setting it up on your own machine in a format suitable for sharing via the IPFS protocol. Typically you'd install IPFS on your own computer and create a new instance of IPFS (also known as a node) there. That's where your data would live locally, referenced by content addresses (CIDs). Data stored in IPFS can take many forms, but one of the most common use cases is the sharing of traditional files, which we'll learn more about in this tutorial.

Whenever you see `ipfs.someMethod()` in our lessons, `ipfs` is a variable that refers to your IPFS instance. When you replicate this process on your own machine, you can choose to call your node `ipfs` or `node` or `myNode` or `zebra`... the choice is yours! What's important to know is that the methods we're showing you will only affect your own instance of IPFS.
You could choose to share your data or files with your peers when you had a network connection, but if you were the only one hosting a particular resource, it would become unavailable to your peers when your machine went offline. Having multiple peers hosting the same files is what makes them more readily available, and using CIDs (unique content identifiers created through cryptograhpic hashing) is what makes this system secure. We'll talk more about sharing in future tutorials, but for now we'll focus on how to work with files within your own IPFS instance.

We're creating your IPFS node behind the scenes so you can focus on the content of our lessons, but eventually you'll need to learn to host your own node locally. When you're ready to experiment, you can find instructions for [installing IPFS](https://docs.ipfs.io/introduction/install/) and [initializing your node](https://docs.ipfs.io/introduction/usage/) in our docs.
## Mutable File System
Because files in IPFS are content-addressed and immutable, you can't edit files; instead, a new file is created with every change. The **Mutable File System (MFS)** is a tool built into IPFS that lets you treat files like you normally would in a name-based filesystem — you can add, remove, move, and edit MFS files and have all the work of updating links and hashes taken care of for you. It's an abstraction that lets you deal with immutable data as if it were mutable.

MFS is accessed through the `files` commands in the IPFS CLI (command-line interface) and API. In this tutorial we'll explore the [Files API](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#the-files-api-aka-mfs-the-mutable-file-system).

If you've worked with files and directories from the command line before, many of the MFS methods will look very familiar!

Let's start exploring how we can work with files in IPFS!
2 changes: 1 addition & 1 deletion src/tutorials/Mutable-File-System/01.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<Lesson
:text="text"
lessonTitle="Working with a local IPFS node">
lessonTitle="Introducing IPFS">
</Lesson>
</template>

Expand Down
2 changes: 1 addition & 1 deletion src/tutorials/Mutable-File-System/02-exercise.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
First, upload one or more files by dropping it below or clicking to make a selection from your file explorer. Next, in the code editor, remove the comment markers that precede `return files.length` within the `run` function, which will make the function calculate how many files have been uploaded (the "length" of an array is the number of items it contains). Open your inspector and select `Console` before you hit the "Submit" button. What do you see?
Run `files.stat` to check the status of your root directory ( `/` ) in IPFS. Be sure to return your result.
Loading