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

Wrapping VaultOps errors in ErrorPolykey errors and updating tests #838

Merged
merged 1 commit into from
Nov 12, 2024

Conversation

aryanjassal
Copy link
Contributor

@aryanjassal aryanjassal commented Nov 1, 2024

Description

Any non-Polykey error, when sent through the RPC, tries to throw raw JSON. In Polykey CLI, this leads to undefined errors.

This PR fixes that by wrapping all errors thrown by the VaultOps operation in an appropriate ErrorPolykey error.

Issues Fixed

Tasks

  • 1. Update all VaultOps to wrap errors in ErrorPolykey
  • [ ] 2. Return SuccessOrErrorMessage instead of string on error in related VaultsSecrets handlers will be done in another PR.
  • [ ] 3. Update tests to be more stringent for all VaultsSecrets handlers will be done in another PR.
  • 4. Make sure all errors are wrapped in a polykey error. This is something the API should guarantee.
  • 5. Spilt VaultsSecretsGet and VaultsSecretsCat
  • [ ] 6. Add tests for error serialisation we don't test utils functions

Final checklist

  • Domain specific tests
  • Full tests
  • Updated inline-comment documentation
  • Lint fixed
  • Squash and rebased
  • Sanity check the final build

@aryanjassal aryanjassal self-assigned this Nov 1, 2024
@aryanjassal
Copy link
Contributor Author

This VaultOps method still exists, which was previously powering the secrets list command, but right now, is not being used anywhere.

What do we do with outdated VaultOps? Do we remove this, or keep this in, just in case this is needed sometime in the future?

/**
 * Retrieves a list of the secrets in a vault
 */
async function listSecrets(vault: Vault): Promise<string[]> {
  return await vault.readF(async (efs) => {
    const secrets: string[] = [];
    for await (const secret of vaultsUtils.readDirRecursively(efs)) {
      secrets.push(secret);
    }
    return secrets;
  });
}

@aryanjassal
Copy link
Contributor Author

We need to make sure that all errors, Polykey or otherwise, are being wrapped in a Polykey error. All Polykey errors which happen during a RPC call are wrapped in ErrorPolykeyRemote. However, non-Polykey errors are propagated as-is. This was also why we had the undefined errors in the first place.

This needs to be reviewed. All errors must be wrapped in ErrorPolykeyRemote to ensure integrity after passing through the RPC. The change for this is needed inside the toError and fromError within the networks domain.

Thoughts, @tegefaulkes ?

@aryanjassal
Copy link
Contributor Author

aryanjassal commented Nov 1, 2024

The VaultsSecretsGet handler is becoming too complex to use with the continueOnError option. It was introduced to retain backwards compatibility, and to ensure that the command behaves like VaultOps.getSecret, but that use case is not used anymore.

However, it is a very common use case, so I will split it into VaultsSecretsGet and VaultsSecretsCat. The VaultsSecretsGet will behave like VaultOps.getSecret, and VaultsSecretsCat will behave like the current VaultsSecretsGet with continueOnError set, which ignores any errors.

Brian mentioned this before, when we were first implementing this command, but at the time, it was an acceptable trade-off. However, I now realise that this introduces a vector where new bugs can very easily be introduced, and it is also inconsistent with every other VaultsSecrets handler, so I could make this change here, but is it a good idea?

Thoughts, @tegefaulkes ?

@aryanjassal
Copy link
Contributor Author

Now when trying to serialise a non-Polykey error, it gets wrapped in ErrorPolykeyUnexpected like this before propagating through the RPC.

    ErrorPolykeyRemote: 
        at toError (/home/aryanj/Projects/polykey/src/network/utils.ts:597:27)
        at Object.transform (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/utils.ts:452:19)
        ... 64 lines matching cause stack trace ...
        at processTicksAndRejections (node:internal/process/task_queues:95:5) {
      data: {},
      cause: ErrorPolykeyUnexpected: Unexpected error of type ErrorEncryptedFSError occured
          at toError (/home/aryanj/Projects/polykey/src/network/utils.ts:589:13)
          at Object.transform (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/utils.ts:452:19)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at transformStreamDefaultControllerPerformTransform (node:internal/webstreams/transformstream:515:18)
          at transformStreamDefaultSinkWriteAlgorithm (node:internal/webstreams/transformstream:565:10)
          at Object.write (node:internal/webstreams/transformstream:360:14)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at writableStreamDefaultControllerProcessWrite (node:internal/webstreams/writablestream:1112:5)
          at writableStreamDefaultControllerAdvanceQueueIfNeeded (node:internal/webstreams/writablestream:1227:5)
          at writableStreamDefaultControllerWrite (node:internal/webstreams/writablestream:1101:3)
          at writableStreamDefaultWriterWrite (node:internal/webstreams/writablestream:991:3)
          at PipeToReadableStreamReadRequest.[kChunk] (node:internal/webstreams/readablestream:1554:31)
          at readableStreamFulfillReadRequest (node:internal/webstreams/readablestream:2096:24)
          at readableStreamDefaultControllerEnqueue (node:internal/webstreams/readablestream:2287:5)
          at transformStreamDefaultControllerEnqueue (node:internal/webstreams/transformstream:496:5)
          at TransformStreamDefaultController.enqueue (node:internal/webstreams/transformstream:306:5)
          at Object.transform (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/middleware.ts:143:20)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at transformStreamDefaultControllerPerformTransform (node:internal/webstreams/transformstream:515:18)
          at transformStreamDefaultSinkWriteAlgorithm (node:internal/webstreams/transformstream:565:10)
          at Object.write (node:internal/webstreams/transformstream:360:14)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at writableStreamDefaultControllerProcessWrite (node:internal/webstreams/writablestream:1112:5)
          at writableStreamDefaultControllerAdvanceQueueIfNeeded (node:internal/webstreams/writablestream:1227:5)
          at writableStreamDefaultControllerWrite (node:internal/webstreams/writablestream:1101:3)
          at writableStreamDefaultWriterWrite (node:internal/webstreams/writablestream:991:3)
          at PipeToReadableStreamReadRequest.[kChunk] (node:internal/webstreams/readablestream:1554:31)
          at readableStreamFulfillReadRequest (node:internal/webstreams/readablestream:2096:24)
          at readableStreamDefaultControllerEnqueue (node:internal/webstreams/readablestream:2287:5)
          at transformStreamDefaultControllerEnqueue (node:internal/webstreams/transformstream:496:5)
          at defaultTransformAlgorithm (node:internal/webstreams/transformstream:345:3)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at transformStreamDefaultControllerPerformTransform (node:internal/webstreams/transformstream:515:18)
          at transformStreamDefaultSinkWriteAlgorithm (node:internal/webstreams/transformstream:565:10)
          at Object.write (node:internal/webstreams/transformstream:360:14)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at writableStreamDefaultControllerProcessWrite (node:internal/webstreams/writablestream:1112:5)
          at writableStreamDefaultControllerAdvanceQueueIfNeeded (node:internal/webstreams/writablestream:1227:5)
          at writableStreamDefaultControllerWrite (node:internal/webstreams/writablestream:1101:3)
          at writableStreamDefaultWriterWrite (node:internal/webstreams/writablestream:991:3)
          at PipeToReadableStreamReadRequest.[kChunk] (node:internal/webstreams/readablestream:1554:31)
          at readableStreamFulfillReadRequest (node:internal/webstreams/readablestream:2096:24)
          at readableStreamDefaultControllerEnqueue (node:internal/webstreams/readablestream:2287:5)
          at transformStreamDefaultControllerEnqueue (node:internal/webstreams/transformstream:496:5)
          at TransformStreamDefaultController.enqueue (node:internal/webstreams/transformstream:306:5)
          at TokenParser.parser.onValue (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/middleware.ts:46:20)
          at TokenParser.emit (/home/aryanj/Projects/polykey/node_modules/@streamparser/json/src/tokenparser.ts:144:12)
          at TokenParser.pop (/home/aryanj/Projects/polykey/node_modules/@streamparser/json/src/tokenparser.ts:130:10)
          at TokenParser.write (/home/aryanj/Projects/polykey/node_modules/@streamparser/json/src/tokenparser.ts:276:16)
          at Tokenizer.write (/home/aryanj/Projects/polykey/node_modules/@streamparser/json/src/tokenizer.ts:193:20)
          at JSONParser.write (/home/aryanj/Projects/polykey/node_modules/@streamparser/json/src/jsonparser.ts:34:20)
          at Object.transform (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/middleware.ts:53:16)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at transformStreamDefaultControllerPerformTransform (node:internal/webstreams/transformstream:515:18)
          at transformStreamDefaultSinkWriteAlgorithm (node:internal/webstreams/transformstream:565:10)
          at Object.write (node:internal/webstreams/transformstream:360:14)
          at ensureIsPromise (node:internal/webstreams/util:185:19)
          at writableStreamDefaultControllerProcessWrite (node:internal/webstreams/writablestream:1112:5)
          at writableStreamDefaultControllerAdvanceQueueIfNeeded (node:internal/webstreams/writablestream:1227:5)
          at writableStreamDefaultControllerWrite (node:internal/webstreams/writablestream:1101:3)
          at writableStreamDefaultWriterWrite (node:internal/webstreams/writablestream:991:3)
          at PipeToReadableStreamReadRequest.[kChunk] (node:internal/webstreams/readablestream:1554:31)
          at readableStreamFulfillReadRequest (node:internal/webstreams/readablestream:2096:24)
          at readableStreamDefaultControllerEnqueue (node:internal/webstreams/readablestream:2287:5)
          at ReadableStreamDefaultController.enqueue (node:internal/webstreams/readablestream:1075:5)
          at constructor_.readablePull (/home/aryanj/Projects/polykey/node_modules/@matrixai/ws/src/WebSocketStream.ts:380:16)
          at processTicksAndRejections (node:internal/process/task_queues:95:5) {
        data: {},
        cause: { type: 'ErrorEncryptedFSError', data: [Object] },
        timestamp: 2024-11-04T06:11:14.216Z,
        exitCode: 76
      },
      timestamp: 2024-11-04T06:11:14.216Z,
      exitCode: 76,
      metadata: {
        localHost: '127.0.0.1',
        localPort: 50070,
        remoteHost: '127.0.0.1',
        remotePort: 45015,
        command: 'vaultsSecretsWriteFile'
      }
    }

Should the error only be wrapped in ErrorPolykeyRemote, or this additional layer fine for wrapping unexpected errors?

@tegefaulkes @CMCDragonkai

@tegefaulkes
Copy link
Contributor

  • Yes, to properly deseralise the error it needs to be an error that is exported by the root errors.ts in polykey. So we need to wrap any errors not in that tree with one that is. Make sure that the error doing the wrapping has a description that describes the error that it wraps.
  • All errors that come through the RPC are wrapped in an ErrorPolykeyRemote. I forget when this is done exactly so you'll need to check. I'm guessing it happens in the toError after the error has been deserialized.
  • If VaultsSecretsGet is getting too complex by trying to address to many cases then you need to split it up into two methods. Sure they do similar things but the behaviour differs too much now. You need to split it up like we discussed before. Make comments having the two methods refer to each other in case an similar behaviour needs to be changed later down the line.

@aryanjassal
Copy link
Contributor Author

All errors that come through the RPC are wrapped in an ErrorPolykeyRemote. I forget when this is done exactly so you'll need to check. I'm guessing it happens in the toError after the error has been deserialized.

I found the relevant part and modified the code to wrap an error inside ErrorPolykeyUnexpected if a non-Polykey error is thrown at the top of the stack, as that would be an unwrapped error. My question is, should the error be wrapped in a new error (like ErrorPolykeyRemote > ErrorPolykeyUnexpected > ErrorEFS) or be wrapped in only ErrorRemotePolykey (like ErrorRemotePolykey > ErrorEFS)?

src/network/utils.ts Outdated Show resolved Hide resolved
src/network/utils.ts Outdated Show resolved Hide resolved
src/client/handlers/VaultsSecretsGet.ts Outdated Show resolved Hide resolved
src/errors.ts Outdated Show resolved Hide resolved
@CMCDragonkai
Copy link
Member

I think the ErrorPolykeyRemote is a generic rpc error that wraps any error coming from the other side. This is supposed to apply for any errors sent to the server side but this hasn't been implemented. See js-rpc issues.

Then what you're doing is wrapping something unknown. Which is not quite the same thing.

Point is an error can technically be anything. Any value of any type can be thrown in JS. Therefore ErrorPolykeyRemote can just wrap it as the cause.

The processor of ErrorPolykeyRemote can interpret anything and therefore should be able to deal with truly unknown things.

Then I believe we already have some sort of Unknown/Undefined/Unexpected sort of error class already defined in the errors.ts in the PK core library.

@CMCDragonkai
Copy link
Member

So always check what we already have before reinventing it. Reinvention leads to entropy.

@aryanjassal
Copy link
Contributor Author

Then I believe we already have some sort of Unknown/Undefined/Unexpected sort of error class already defined in the errors.ts in the PK core library.

Yes, we do have an ErrorPolykeyUnknown error implemented, but it's description says 'Unable to deserialise to known error', so it's function is slightly different than what is being handled here, where the error was unexpected.

@CMCDragonkai
Copy link
Member

Yea I think I meant that to mean I don't know what exception to deserialize to... that is over the RPC system, if you do throw 123; then 123 is the error that is unknown.

@aryanjassal
Copy link
Contributor Author

aryanjassal commented Nov 5, 2024

I have encountered an interesting issue with wrapping throw 123. When I throw regular errors, they parse properly, but when I throw a non-error (like 123), I get this error:

    Received constructor: ErrorRPCParse

    Received message: "structure did not match a `JSONRPCResponse`"

          at parseJSONRPCResponse (node_modules/@matrixai/rpc/src/utils.ts:190:10)
          at TokenParser.parser.onValue (node_modules/@matrixai/rpc/src/middleware.ts:45:29)
          at TokenParser.emit (node_modules/@streamparser/json/src/tokenparser.ts:144:12)
          at TokenParser.pop (node_modules/@streamparser/json/src/tokenparser.ts:130:10)
          at TokenParser.write (node_modules/@streamparser/json/src/tokenparser.ts:276:16)
          at Tokenizer.write (node_modules/@streamparser/json/src/tokenizer.ts:193:20)
          at JSONParser.write (node_modules/@streamparser/json/src/jsonparser.ts:34:20)
          at Object.transform (node_modules/@matrixai/rpc/src/middleware.ts:54:8)
          at constructor_.readablePull (node_modules/@matrixai/ws/src/WebSocketStream.ts:380:16)

I am investigating the RPC code as I'm not sure what can be causing this from Polykey side, but it could be as easily caused by Polykey, too.

After analysing the incoming JSON responses, I got this:

(for 123)

{
  "jsonrpc": "2.0",
  "error": { 
    "code": -32006,
    "data": {
      "type": "ErrorPolykeyUnexpected",
      "data": {
        "message": "Unexpected error occured: 123",
        "timestamp":"2024-11-05T08:14:47.221Z",
        "data": { "reason": 123 },
        "stack": "ErrorPolykeyUnexpected: Unexpected error occured: 123\n    at constructor_.fromError (/home/aryanj/Projects/polykey/src/network/utils.ts:498:26)\n    at Object.pull (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/RPCServer.ts:331:28)",
        "description": "An error originating outside Polykey was thrown",
        "exitCode": 255
      }
    }
  },
  "id": null
}

(reason for error)

{
    jsonrpc: "2.0",
    error: {
      code: -32006,
      data: { type: "ErrorPolykeyUnexpected", data: [Object] }
  },
  id: null
}

(for ErrorEFS)

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32006,
    "message": "ENOENT: no such file or directory, dir/secret-name",
    "data": {
      "type": "ErrorPolykeyUnexpected",
      "data": {
        "message": "Unexpected error occurred: ErrorEncryptedFSError",
        "timestamp": "2024-11-05T08:04:32.830Z",
        "data": {},
        "cause": {
          "type": "ErrorEncryptedFSError",
            "data": {
              "message": "ENOENT: no such file or directory, dir/secret-name",
              "timestamp":"2024-11-05T08:04:32.724Z",
              "data": {},
              "stack": "ErrorEncryptedFSError: ENOENT: no such file or directory, dir/secret-name\n    at constructor_._open (/home/aryanj/Projects/polykey/node_modules/encryptedfs/src/EncryptedFS.ts:2151:17)\n    at /home/aryanj/Projects/polykey/node_modules/encryptedfs/src/EncryptedFS.ts:1930:15\n    at Object.maybeCallback (/home/aryanj/Projects/polykey/node_modules/encryptedfs/src/utils.ts:405:12)\n    at /home/aryanj/Projects/polykey/node_modules/encryptedfs/src/EncryptedFS.ts:3421:19\n    at Object.maybeCallback (/home/aryanj/Projects/polykey/node_modules/encryptedfs/src/utils.ts:405:12)\n    at /home/aryanj/Projects/polykey/src/vaults/VaultOps.ts:306:5\n    at /home/aryanj/Projects/polykey/src/vaults/VaultInternal.ts:475:9\n    at withF (/home/aryanj/Projects/polykey/node_modules/@matrixai/resources/src/utils.ts:24:12)\n    at withF (/home/aryanj/Projects/polykey/node_modules/@matrixai/resources/src/utils.ts:24:12)\n    at Object.writeSecret (/home/aryanj/Projects/polykey/src/vaults/VaultOps.ts:305:3)\n    at /home/aryanj/Projects/polykey/src/client/handlers/VaultsSecretsWriteFile.ts:41:11\n    at /home/aryanj/Projects/polykey/src/vaults/VaultManager.ts:1032:14\n    at withF (/home/aryanj/Projects/polykey/node_modules/@matrixai/resources/src/utils.ts:24:12)\n    at constructor_.withVaults (/home/aryanj/Projects/polykey/src/vaults/VaultManager.ts:1025:12)\n    at /home/aryanj/Projects/polykey/src/client/handlers/VaultsSecretsWriteFile.ts:38:7\n    at withF (/home/aryanj/Projects/polykey/node_modules/@matrixai/resources/src/utils.ts:24:12)\n    at handle (/home/aryanj/Projects/polykey/src/client/handlers/VaultsSecretsWriteFile.ts:27:5)\n    at wrapperDuplex (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/RPCServer.ts:398:15)\n    at outputGen (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/RPCServer.ts:304:26)\n    at Object.pull (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/RPCServer.ts:320:37)",
              "_errno": 34,
              "_code": "ENOENT",
              "_description": "no such file or directory",
              "_syscall":"open"
            }
          },
          "stack": "ErrorPolykeyUnexpected: Unexpected error occurred: ErrorEncryptedFSError\n    at constructor_.fromError (/home/aryanj/Projects/polykey/src/network/utils.ts:480:26)\n    at Object.pull (/home/aryanj/Projects/polykey/node_modules/@matrixai/rpc/src/RPCServer.ts:331:28)",
          "description": "An error originating outside Polykey was thrown",
          "exitCode": 255
        }
      }
    },
    "id": null
}

(reason for passing)

{
    jsonrpc: "2.0",
    error: {
      code: -32006,
      message: "ENOENT: no such file or directory, dir/secret-name",
      data: { type: "ErrorPolykeyUnexpected", data: [Object] }
    },
    id: null
  }

After investigating, for some reason, the message field is missing when I try to wrap non-errors in ErrorPolykeyUnexpected.

@tegefaulkes can you hint me in the right direction to resolve this? I have pushed up the latest code for review if required.

@tegefaulkes
Copy link
Contributor

I'm not sure if you need my direction on this. If message is required but missing in the RPC message then that's the problem. Look into why it's missing. If I had to guess, the message is expected to be defined but through some shenanigans its undefined. When serialised to JSON the field is just removed from the message.

@CMCDragonkai
Copy link
Member

Yes if a field is undefined value, JSON serialisation removes the key entirely.

The real question is why is it producing undefined at all, BEFORE it gets to the JSON serialisation.

@aryanjassal
Copy link
Contributor Author

// Extract from js-rpc (src/RPCServer.ts:317-346)
const reverseMiddlewareStream = new ReadableStream<JSONRPCResponse>({
  pull: async (controller) => {
    try {
      const { value, done } = await outputGenerator.next();
      if (done) {
        controller.close();
        return;
      }
      controller.enqueue(value);
    } catch (e) {
      try {
        const rpcError: JSONRPCResponseError = {
          code: errors.JSONRPCResponseErrorCode.RPCRemote,
          message: e.message,
          data: this.fromError(e),
        };
        const rpcErrorMessage: JSONRPCResponseFailed = {
          jsonrpc: '2.0',
          error: rpcError,
          id,
        };
        controller.enqueue(rpcErrorMessage);
      } catch (e) {
        this.dispatchEvent(
          new events.RPCErrorEvent({
            detail: e,
          }),
        );
        controller.error(e);
      }
    }
  }
});

I finally pinpointed the reason for this issue. And the issue is in js-rpc itself and not in my code.

In the snippet I have provided, you can see that there is a try-catch block to try and get the next value to read, or if an error occurs, then do the error serialisation. The issue is in this part.

When I do throw 123, it is caught by the catch block. As such, the value of e is 123. When trying to serialise it, for the message, we assign it e.message. It would normally be defined for all Error instances, but this is not the case if we throw non-Error objects. This results in message being undefined, and being omitted from the message, and breaking the parsing on the other side.

I tried an easy fix here by adding a check for undefined values. It worked perfectly.

const rpcError: JSONRPCResponseError = {
  code: errors.JSONRPCResponseErrorCode.RPCRemote,
  message: e.message ?? '', // Use an empty string if the message is undefined
  data: this.fromError(e),
};

I will make a commit to js-rpc with this fix.

@CMCDragonkai @tegefaulkes

@tegefaulkes
Copy link
Contributor

Ah I see. The undefined message will need to be fixed in rpc and the fromError method. We don't want to just stub it as an empty string if there is no message. Provide a more descriptive message in that case. Like thrown error was a literal ${e} for primitive values.

@aryanjassal aryanjassal marked this pull request as ready for review November 7, 2024 02:18
@aryanjassal
Copy link
Contributor Author

This PR kinda relies on MatrixAI/js-rpc#69

@CMCDragonkai
Copy link
Member

Should the message be an empty string if the e.message doesn't exist? Shouldn't it be undefined? The e is therefore not an exception you'd have to handle this somewhere.

@aryanjassal
Copy link
Contributor Author

aryanjassal commented Nov 7, 2024

Should the message be an empty string if the e.message doesn't exist? Shouldn't it be undefined? The e is therefore not an exception you'd have to handle this somewhere.

In cases where e isn't an actual exception, in js-rpc, the message is indeed undefined, which results in the field being omitted from being transferred, and the message not being properly parsable.

To deal with this, the PR in js-rpc actually intelligently creates a message if it doesn't exist on the actual error. For example, if we do throw 123, the generated message would be 'Non-error literal 123 was thrown'. And if we do throw new ReadableStream(), then the error message reads, 'Non-error object ReadableStream was thrown'.

This was done to support properly wrapping throw 123 from the handler itself, as otherwise, doing this would result in an error on the library's side.

I can also implement something similar in Polykey itself, where it can intelligently synthesise an error message based on the error type and value.

@aryanjassal
Copy link
Contributor Author

This PR is basically done. This needs a final review, and if nothing crazy crops up in this, then we can get this merged. Merging this will allow resuming work on the CLI side, although the work there is also mostly done. All I had to do in the CLI is update the way these errors are rendered. Maybe that also needs a bit of discussion?

@tegefaulkes
Copy link
Contributor

I had a look. Nothing jumps out at me. Should be good to merge.

tests: added tests for VaultsSecretsWriteFile

chore: updated secrets remove to properly operate on multiple paths

chore: working on proper error wrapping for unexpected errors

chore: added comments and cleaned up VaultOps

chore: split vaultsSecretsGet and its tests

chore: dealing with an edge case when error cannot be serialised

chore: cleaned up files

chore: added intelligent error message synthesis

deps: updated version of @matrixai/rpc

fix: handle attempt to remove vault root
@aryanjassal
Copy link
Contributor Author

This PR has been approved and all the CI is also passing. Once MatrixAI/Polykey-CLI#320 has also been approved, we can merge this.

@aryanjassal aryanjassal merged commit 24dd12f into staging Nov 12, 2024
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Some secrets commands throw an undefined error
3 participants