User Management¶
mdfs has a built-in multi-user system with Unix-style permissions. This guide covers creating users, managing groups, setting permissions, and controlling access.
Default State¶
A fresh mdfs instance has one user and two groups:
| Entity | ID | Notes |
|---|---|---|
root user |
uid=0 | Superuser, bypasses all permission checks |
root group |
gid=0 | Root's primary group |
wheel group |
gid=1 | Admin group — members can create users/groups |
On first CLI launch, you create an admin user who is automatically added to the wheel group.
Creating Users¶
Regular Users¶
Any admin (root or wheel member) can create users:
Each new user automatically gets:
- A unique uid (auto-incremented)
- A personal group with the same name (e.g., group bob with a unique gid)
- A home directory at /home/<name> owned by the user
- Default permissions to read public files and create files in directories they own
Agent Users¶
Agents are special users designed for programmatic access (scripts, bots, CI):
alice@markdownfs:/ $ addagent deploy-bot
Created agent: deploy-bot (uid=3)
Token: a1b2c3d4e5f6... (save this — shown only once)
The token is used for HTTP API authentication via Authorization: Bearer <token>. The raw token is shown once — mdfs only stores its SHA-256 hash internally.
Deleting Users¶
Only root can delete users:
Root cannot be deleted.
Managing Groups¶
Creating Groups¶
Adding Users to Groups¶
Removing Users from Groups¶
Deleting Groups¶
Only root can delete groups. The root and wheel groups are protected:
Viewing Group Memberships¶
Viewing User Identity¶
alice@markdownfs:/ $ whoami
alice
alice@markdownfs:/ $ id
uid=1(alice) gid=2(alice) groups=2(alice),1(wheel)
alice@markdownfs:/ $ id bob
uid=2(bob) gid=3(bob) groups=3(bob),4(engineering)
Switching Users¶
Admins (root or wheel members) can switch to any user:
Permission Model¶
mdfs uses standard Unix-style permission bits: owner, group, and other, each with read (r), write (w), and execute (x).
Permission Bits¶
| Bit | Octal | File | Directory |
|---|---|---|---|
| Read (r) | 4 | View content (cat) |
List entries (ls) |
| Write (w) | 2 | Modify content (write) |
Add/remove entries (touch, rm, mv) |
| Execute (x) | 1 | (unused for files) | Traverse (cd, path resolution) |
Default Permissions¶
| Type | Mode | Human-readable |
|---|---|---|
| New file | 0644 |
rw-r--r-- — owner reads/writes, everyone else reads |
| New directory | 0755 |
rwxr-xr-x — owner has full access, everyone else can list and traverse |
| New symlink | 0777 |
rwxrwxrwx — unrestricted (target permissions still apply) |
Reading Permissions in ls -l¶
alice@markdownfs:/ $ ls -l
drwxr-xr-x alice engineering 3 Apr 13 10:30 project/
-rw-r--r-- alice alice 42 Apr 13 10:31 readme.md
lrwxrwxrwx alice alice 9 Apr 13 10:32 link.md -> readme.md
The first column reads as: [type][owner rwx][group rwx][other rwx]
d= directory,l= symlink,-= filerwx= read, write, execute enabled-= that bit is not set
Changing Permissions with chmod¶
Use octal notation. Only the file owner or root can change permissions:
Common permission patterns:
| Mode | Meaning | Use Case |
|---|---|---|
755 |
rwxr-xr-x |
Shared directory — everyone can read and traverse |
700 |
rwx------ |
Private directory — owner only |
644 |
rw-r--r-- |
Shared file — everyone can read |
600 |
rw------- |
Private file — owner only |
664 |
rw-rw-r-- |
Group-writable file |
775 |
rwxrwxr-x |
Group-writable directory |
Changing Ownership with chown¶
# Change owner (root only)
root@markdownfs:/ $ chown bob readme.md
# Change owner and group
root@markdownfs:/ $ chown bob:engineering readme.md
# Change group only (owner can do this if they're in the target group)
alice@markdownfs:/ $ chown alice:engineering project/
Rules: - Changing the owner requires root - Changing the group requires root, OR the current owner must be a member of the target group
Viewing File Metadata with stat¶
alice@markdownfs:/ $ stat readme.md
File: readme.md
Size: 42
Type: file
Inode: 5
Mode: 0644
Uid: 1 (alice)
Gid: 2 (alice)
Created: 2025-04-13 10:30:00
Modified: 2025-04-13 10:31:15
Special Permission Bits¶
Sticky Bit (1xxx)¶
When set on a directory, only the file owner, directory owner, or root can delete or rename files within it. Useful for shared directories:
Now anyone can create files in shared/, but users can only delete their own files.
Setgid Bit (2xxx)¶
When set on a directory, new files and subdirectories inherit the directory's group instead of the creator's primary group. Subdirectories also inherit the setgid bit:
alice@markdownfs:/ $ mkdir team-docs
alice@markdownfs:/ $ chown alice:engineering team-docs
alice@markdownfs:/ $ chmod 2775 team-docs
# Bob creates a file — it gets group "engineering" automatically
bob@markdownfs:/ $ touch team-docs/notes.md
bob@markdownfs:/ $ ls -l team-docs/
-rw-r--r-- bob engineering 0 Apr 13 11:00 notes.md
Delegation (Agent Impersonation)¶
Agents can act on behalf of users using the delegation system. When delegated, all operations use the intersection of the agent's and the delegated user's permissions (least-privilege):
deploy-bot@markdownfs:/ $ delegate bob
Delegating as bob
deploy-bot@markdownfs:/ $ touch docs/deploy-notes.md
# File is owned by bob, and both deploy-bot AND bob must have permission
deploy-bot@markdownfs:/ $ undelegate
Delegation cleared
You can also delegate for a group:
Permission Requirements by Operation¶
| Operation | Required Permission |
|---|---|
ls, tree |
Read + Execute on directory |
cd |
Execute on target directory |
cat |
Read on file |
touch (new file) |
Write + Execute on parent directory |
touch (existing) |
Write on file |
write (new file) |
Write on parent directory |
write (existing) |
Write on file |
mkdir |
Write + Execute on parent directory |
rm |
Write + Execute on parent directory |
mv |
Write + Execute on both source and destination parents |
cp |
Read on source, Write + Execute on destination parent |
chmod |
Must be owner or root |
chown (uid) |
Root only |
ln -s |
Write + Execute on link's parent directory |
grep, find |
Read on file/directory |
| Path traversal | Execute on every intermediate directory in the path |
Practical Example: Team Setup¶
Here's a complete walkthrough setting up a team workspace. Since top-level directories are owned by root, we switch to root to create the shared structure:
# Start as admin
alice@markdownfs:~ $
# Create team members (each gets a home directory automatically)
alice@markdownfs:~ $ adduser bob
User 'bob' created (uid=2)
Home directory: /home/bob
alice@markdownfs:~ $ adduser carol
alice@markdownfs:~ $ addagent ci-bot
# Create a shared team group
alice@markdownfs:~ $ addgroup dev-team
alice@markdownfs:~ $ usermod -aG dev-team alice
alice@markdownfs:~ $ usermod -aG dev-team bob
alice@markdownfs:~ $ usermod -aG dev-team carol
# Switch to root to create shared top-level directories
alice@markdownfs:~ $ su root
# Create a shared workspace with setgid
root@markdownfs:~ $ mkdir /project
root@markdownfs:~ $ chown alice:dev-team /project
root@markdownfs:~ $ chmod 2775 /project
# Create a drop-box directory (sticky bit — anyone can add, only owners can delete)
root@markdownfs:~ $ mkdir /submissions
root@markdownfs:~ $ chmod 1777 /submissions
# Switch back to alice
root@markdownfs:~ $ su alice
# Alice can also create private dirs in her home
alice@markdownfs:~ $ mkdir private
alice@markdownfs:~ $ chmod 700 private
# Bob creates a file in the shared project — inherits dev-team group
alice@markdownfs:~ $ su bob
bob@markdownfs:~ $ touch /project/design.md
bob@markdownfs:~ $ write /project/design.md # System Design Draft
bob@markdownfs:~ $ ls -l /project/
-rw-r--r-- bob dev-team 22 Apr 13 11:05 design.md