diff --git a/README.md b/README.md
index f24a937..4667259 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,15 @@
# CrystalKeep
+
A comprehensive solution for encrypted data storage.
+
+# Features
+
+- Simple Key -> Value storage
+- Username / password logins
+- Files / Directories
+
+# Design
+
+CrystalKeep makes use of _Shards_ as the most basic form of encrypted data storage. A shard is a single data item, like login credentials, an image, or some text. One or more shards are stored in a _Cluster_, which is essentially a collection of shards (and possibly nested clusters). Top-level clusters (not nested inside another) can be encrypted and saved with a secret key passphrase.
+
+With this approach, the user minimizes the amount of data that is accessible to an attacker in the event that the attacker gets access to the system while the contents of a cluster are unencrypted in memory, since only one cluster may be actively open at a time.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f5ff024
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ nl.andrewlalis
+ crystalkeep
+ 1.0-SNAPSHOT
+
+
+ 11
+ 11
+ UTF-8
+ UTF-8
+ 16
+
+
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.4
+
+ nl.andrewlalis.crystalkeep.CrystalKeep
+
+
+
+
+
+
+
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.20
+ provided
+ true
+
+
+
\ No newline at end of file
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/CrystalKeep.java b/src/main/java/nl/andrewlalis/crystalkeep/CrystalKeep.java
new file mode 100644
index 0000000..e13a7e4
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/CrystalKeep.java
@@ -0,0 +1,7 @@
+package nl.andrewlalis.crystalkeep;
+
+public class CrystalKeep {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/model/Cluster.java b/src/main/java/nl/andrewlalis/crystalkeep/model/Cluster.java
new file mode 100644
index 0000000..ccba5d4
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/model/Cluster.java
@@ -0,0 +1,41 @@
+package nl.andrewlalis.crystalkeep.model;
+
+import lombok.Getter;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+@Getter
+public class Cluster {
+ private final Set shards;
+ private final Set clusters;
+ private final Cluster parent;
+
+ public Cluster(Set shards, Set clusters, Cluster parent) {
+ this.shards = shards;
+ this.clusters = clusters;
+ this.parent = parent;
+ if (this.parent != null) {
+ this.parent.addCluster(this);
+ }
+ }
+
+ public Cluster() {
+ this(new HashSet<>(), new HashSet<>(), null);
+ }
+
+ public void addShard(Shard shard) {
+ if (!this.equals(shard.getCluster())) {
+ throw new IllegalArgumentException("Shard must have correct cluster set before adding it to shard.");
+ }
+ this.shards.add(shard);
+ }
+
+ public void addCluster(Cluster cluster) {
+ if (!this.equals(cluster.getParent())) {
+ throw new IllegalArgumentException("Cluster must have correct parent set before adding it to cluster.");
+ }
+ this.clusters.add(cluster);
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/model/ClusterSerializer.java b/src/main/java/nl/andrewlalis/crystalkeep/model/ClusterSerializer.java
new file mode 100644
index 0000000..e311c1d
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/model/ClusterSerializer.java
@@ -0,0 +1,44 @@
+package nl.andrewlalis.crystalkeep.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+
+public class ClusterSerializer {
+ public byte[] toBytes(Cluster cluster) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ bos.write(this.toBytes(cluster.getClusters().size()));
+ for (Cluster child : cluster.getClusters()) {
+ bos.write(this.toBytes(child));
+ }
+ bos.write(this.toBytes(cluster.getShards().size()));
+ for (Shard shard : cluster.getShards()) {
+ bos.write(shard.toBytes());
+ }
+ return bos.toByteArray();
+ }
+
+ public Cluster fromBytes(ByteArrayInputStream bis, Cluster parent) throws IOException {
+ Cluster cluster = new Cluster(new HashSet<>(), new HashSet<>(), parent);
+ int childCount = this.toInt(bis.readNBytes(4));
+ for (int i = 0; i < childCount; i++) {
+ cluster.addCluster(this.fromBytes(bis, cluster));
+ }
+ int shardCount = this.toInt(bis.readNBytes(4));
+ for (int i = 0; i < shardCount; i++) {
+ cluster.addShard(Shard.fromBytes(bis));
+ }
+ return cluster;
+ }
+
+ public byte[] toBytes(int x) {
+ return ByteBuffer.allocate(4).putInt(x).array();
+ }
+
+ public int toInt(byte[] bytes) {
+ assert(bytes.length == 4);
+ return ByteBuffer.wrap(bytes).getInt();
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/model/Shard.java b/src/main/java/nl/andrewlalis/crystalkeep/model/Shard.java
new file mode 100644
index 0000000..d4db489
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/model/Shard.java
@@ -0,0 +1,37 @@
+package nl.andrewlalis.crystalkeep.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.ByteArrayInputStream;
+import java.time.LocalDateTime;
+
+@Getter
+public abstract class Shard implements Comparable {
+ private final Cluster cluster;
+ @Setter
+ private String name;
+ private final LocalDateTime createdAt;
+
+ public Shard(Cluster cluster, String name, LocalDateTime createdAt) {
+ this.cluster = cluster;
+ this.cluster.addShard(this);
+ this.name = name;
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public int compareTo(Shard o) {
+ int r = this.getName().compareTo(o.getName());
+ if (r != 0) return r;
+ return this.getCreatedAt().compareTo(o.getCreatedAt());
+ }
+
+ public byte[] toBytes() {
+ return new byte[0];
+ }
+
+ public static Shard fromBytes(ByteArrayInputStream bis) {
+ return null;
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/model/shards/LoginCredentialsShard.java b/src/main/java/nl/andrewlalis/crystalkeep/model/shards/LoginCredentialsShard.java
new file mode 100644
index 0000000..afba973
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/model/shards/LoginCredentialsShard.java
@@ -0,0 +1,21 @@
+package nl.andrewlalis.crystalkeep.model.shards;
+
+import lombok.Getter;
+import lombok.Setter;
+import nl.andrewlalis.crystalkeep.model.Cluster;
+import nl.andrewlalis.crystalkeep.model.Shard;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class LoginCredentialsShard extends Shard {
+ private String username;
+ private String password;
+
+ public LoginCredentialsShard(Cluster cluster, String name, LocalDateTime createdAt, String username, String password) {
+ super(cluster, name, createdAt);
+ this.username = username;
+ this.password = password;
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/crystalkeep/model/shards/TextShard.java b/src/main/java/nl/andrewlalis/crystalkeep/model/shards/TextShard.java
new file mode 100644
index 0000000..c2d35b6
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/crystalkeep/model/shards/TextShard.java
@@ -0,0 +1,15 @@
+package nl.andrewlalis.crystalkeep.model.shards;
+
+import nl.andrewlalis.crystalkeep.model.Cluster;
+import nl.andrewlalis.crystalkeep.model.Shard;
+
+import java.time.LocalDateTime;
+
+public class TextShard extends Shard {
+ private String text;
+
+ public TextShard(Cluster cluster, String name, LocalDateTime createdAt, String text) {
+ super(cluster, name, createdAt);
+ this.text = text;
+ }
+}
diff --git a/src/main/resources/crystalkeep.fxml b/src/main/resources/crystalkeep.fxml
new file mode 100644
index 0000000..2f5efbf
--- /dev/null
+++ b/src/main/resources/crystalkeep.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+