@Override
    public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws IOException {
        List<NodeEntry<String, N>> held = new ArrayList<>();
        try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.GROUPS))) {
            stream.filter(this.fileExtensionFilter)
                    .forEach(file -> {
                        String fileName = file.getFileName().toString();
                        try {
                            registerFileAction(StorageLocation.GROUPS, file);
                            ConfigurationNode object = readFile(file);
                            String holder = fileName.substring(0, fileName.length() - this.fileExtension.length());
                            Set<Node> nodes = readNodes(object);
                            for (Node e : nodes) {
                                N match = constraint.match(e);
                                if (match != null) {
                                    held.add(NodeEntry.of(holder, match));
                                }
                            }
                        } catch (Exception e) {
                            this.plugin.getLogger().severe(
                                    "Exception whilst searching group nodes",
                                    new FileIOException(file.getFileName().toString(), e)
                            );
                        }
                    });
        }
        return held;
    }
Flat-file storage using Configurate {@link ConfigurationNode}s.
The data for each user/group/track is stored in a separate file.
common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/SeparatedConfigurateStorage.java